写点什么

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

  • 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:306936
用户头像

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

关注

评论

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

Eplan是电气设计软件吗?Eplan介绍-Eplan官网

智造软件

EPLAN 电气辅助设计 电气设计

2023年大数据平台数据安全厂商汇总

行云管家

大数据 数据安全 大数据平台

一体化打通销售到项目交付 项目型营销更在行

用友BIP

项目云 数智营销

人工智能新范式,创新生产力崛起

百度开发者中心

人工智能 数据分析、 ChatGPT 文心一言

MES系统详细介绍

万界星空科技

MES系统

Mac电脑油猴插件 Tampermonkey激活中文版

胖墩儿不胖y

Mac软件 油猴插件 脚本管理

mac端日程和任务管理 Things3 免激活中文

mac大玩家j

任务管理 Mac软件 任务管理器

提高生产力,创新工作方式

百度开发者中心

人工智能 ChatGPT 生成式AI 文心一言

创新未来的智能数据生成

百度开发者中心

人工智能 ChatGPT 生成式AI

聊聊Kafka的生产者消费者确认机制

高端章鱼哥

kafka

自动创作新内容的人工智能

百度开发者中心

人工智能 数据可视化 生成式AI 文心一言

自动化性能测试的理解误区

老张

性能测试 自动化测试 性能基线

GaussDB技术解读丨数据库迁移创新实践

华为云开发者联盟

数据库 后端 华为云 华为云开发者联盟 企业号9月PK榜

用友iuap数据中台,让数据成为企业价值资产,驱动业务持续创新

用友BIP

2023全球商业创新大会 升级数智底座

MatrixOne WAL设计解析

MatrixOrigin

分布式数据库 云原生数据库 MatrixOrigin MatrixOne HTAP数据库

从数据出发,构建可信赖的生成式AI应用

百度开发者中心

人工智能 数据分析 千帆大模型平台

杰哥教你面试之一百问系列:java中高级多线程concurrent的使用

程序那些事

Java 多线程 并发 程序那些事 面试秘籍

EasyPhoto:基于 SD WebUI 的艺术照生成插件来啦!

阿里云大数据AI技术

阿里云 AI

云堡垒机收费模式有哪些?哪款更灵活?

行云管家

云计算 网络安全 堡垒机 云堡垒机

聊聊GaussDB AP是如何执行SQL的

华为云开发者联盟

数据库 后端 华为云 华为云开发者联盟 企业号9月PK榜

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