在 2025 收官前,看清 Data + AI 的真实走向,点击查看 BUILD 大会精华版 了解详情
写点什么

代码之丑(十二)-- 无状态方法

  • 2012-06-17
  • 本文字数:1303 字

    阅读完需:约 4 分钟

诸位 Java 程序员,想必大家对 SimpleDateFormat 并不陌生。不过,你是否知道,SimpleDateFormat 不是线程安全的(thread safe)。这意味着,下面的代码是错误的:

复制代码
class Sample {
private static final DateFormat format = new SimpleDateFormat("yyyy.MM.dd");
public String getCurrentDateText() {
return format.format(new Date());
}
}

从功能的角度上看,单独执行这段代码是没有问题的,但放到多线程环境下,因为 SimpleDateFormat 不是线程安全的,这段代码就会出错。所以,要想让这段代码正确,我们只要稍做微调:

复制代码
public class Sample {
public String getCurrentDateText() {
return new SimpleDateFormat("yyyy.MM.dd").format(new Date());
}
}

不知你是否注意到,这里的调整只是由原来的共享 format 这个变量,变成了每次调用这个方法时创建出一个新的 SimpleDateFormat 变量。

作为一个专业程序员,我们当然知道,相比于共享一个变量的开销要比每次创建小。之所以我们必须这么做,是因为 SimpleDateFormat 不是线程安全的。但从 SimpleDateFormat 提供给我们的接口上来看,实在让人看不出它与线程安全有和相干。那接下来,我们就要打开 JDK 的源码,看一下其中的代码之丑。

如果你手头没有 JDK 的源码,这里是个不错的参考。

在 format 方法里,有这样一段代码:

复制代码
calendar.setTime(date);

其中,calendar 是 DateFormat 的 protected 字段。这条语句改变了 calendar,稍后,calendar 还会用到(在 subFormat 方法里),而这就是引发问题的根源。

想象一下,在一个多线程环境下,有两个线程持有了同一个 SimpleDateFormat 的实例,分别调用 format 方法:

  1. 线程 1 调用 format 方法,改变了 calendar 这个字段。
  2. 中断来了。
  3. 线程 2 开始执行,它也改变了 calendar。
  4. 又中断了。
  5. 线程 1 回来了,此时,calendar 已然不是它所设的值,而是走上了线程 2 设计的道路。
  6. BANG!!! 稍微花点时间分析一下 format 的实现,我们便不难发现,用到 calendar,唯一的好处,就是在调用 subFormat 时,少了一个参数,却带来了这许多的问题。其实,只要在这里用一个局部变量,一路传递下去,所有问题都将迎刃而解。

这个问题背后隐藏着一个更为重要的问题:无状态。

无状态方法的好处之一,就是它在各种环境下,都可以安全的调用。衡量一个方法是否是有状态的,就看它是否改动了其它的东西,比如全局变量,比如实例的字段。format 方法在运行过程中改动了 SimpleDateFormat 的 calendar 字段,所以,它是有状态的。

写程序,我们要尽量编写无状态方法。

作者简介

郑晔,ThoughtWorks 公司首席咨询师,拥有十多年企业级软件开发经验,热衷于探索各种程序设计语言在真实软件开发中所能发挥的威力,致力于探寻合理的软件开发方式,加入 ThoughtWorks 公司后,投入到敏捷开发方法的实践之中,为其他公司提供敏捷开发方法方面的咨询服务。他的 blog 是梦想风暴,其微博是 @dreamhead

查看原文:代码之丑(十二)


感谢张凯峰对本文的审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ )或者腾讯微博( @InfoQ )关注我们,并与我们的编辑和其他读者朋友交流。

2012-06-17 21:307435
用户头像

发布了 22 篇内容, 共 14.9 次阅读, 收获喜欢 49 次。

关注

评论

发布
暂无评论
发现更多内容

《福布斯》评英特尔最新财报:业绩之外,制程路线图的稳步推进是真正的好消息

科技之家

找 ChatGPT 写 SQL? 不如试试 PromQL,三行解决复杂时序场景查询

Greptime 格睿科技

sql 云原生 时序数据库 PromQL 可观测

为什么选择免费文件共享方法上的托管文件传输

镭速

关于 OAuth 你又了解哪些?

API7.ai 技术团队

api 网关 Oauth APISIX

Elasticsearch 利用 Enrich Processor 实现 MYSQL Join 的操作,支持 Nested 类型,具体应用。

alexgaoyh

MySQL elasticsearch ES 多表join enrich processor

参加大数据培训有什么好处

小谷哥

3Dmax和C4d有什么区别?

Finovy Cloud

3DMAX

3D可视化:18个WebGL框架和Web3D图形库

2D3D前端可视化开发

WebGL 三维可视化 web3d 3d绘图引擎

“云”上书店,氛围感拉满!

天翼云开发者社区

《解构领域驱动设计》-领域驱动设计统一过程

珑彧

Java 读书笔记 架构 方法论 领域驱动设计

腾讯架构师极力推荐:Java多线程与Socket实战微服务框架

小小怪下士

Java 程序员 socket 多线程

RESTful API 为何成为顶流 API 架构风格?

API7.ai 技术团队

API APISIX REST API

线下学习前端培训机构怎么选?

小谷哥

文本数据标注已上线,支持文本分类、实体识别|ModelWhale 版本更新

ModelWhale

人工智能 机器学习 云计算 数据分析 编程建模

领导者!天翼云蝉联政务公有云基础设施市场第一

天翼云开发者社区

共铸国云智领未来| 数据进超市,海岛更善治

天翼云开发者社区

初学者前端培训机构需要注意什么

小谷哥

大数据培训的方法有哪些

小谷哥

java培训与线上自学哪个比较好?

小谷哥

为什么工厂要投资设备智能维护管理技术?

PreMaint

设备管理 设备预测性维护

您有一个MatrixOne Beta Program专属权益待领取

MatrixOrigin

MatrixOrigin MatrixOne betaprogram MO企划

【SpringBoot】简述springboot项目启动数据加载内存中的三种方法

No8g攻城狮

springboot spring-boot

天翼云第八代云主机助力企业攻克上云“大象流”加密处理业务难题

天翼云开发者社区

如何将物理机Windows系统迁移到VMware虚拟机?

天翼云开发者社区

Knative Autoscaler 自定义弹性伸缩

天翼云开发者社区

高性能网络SIG月度动态:SMC 与 IBM 就扩展协议达成一致,virtio 支持 XDP 新特性

OpenAnolis小助手

开源 高性能网络 virtio 龙蜥sig smc

代码之丑(十二)--无状态方法_Java_郑晔_InfoQ精选文章