速来报名!AICon北京站鸿蒙专场~ 了解详情
写点什么

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

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

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

关注

评论

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

双11在即,分享一些稳定性保障技术干货

老张

系统稳定性 大促 生产环境全链路压测

2020 年需要关注的 5 大 Android 开发技术(1),Android知识总结

android 程序员 移动开发

2020-Android-面试重难点(万字篇),android屏幕适配的五种方式

android 程序员 移动开发

云上远程运维的最后那点担心,“云梯”帮你解决

华为云开发者联盟

运维 华为云Stack 远程运维 安全可信 云梯

TDSQL将发布免费版本,助力国产数据库生态完善

腾讯云数据库

数据库 tdsql

2020Android 开发年度总结:“这一年里我到底做了些啥,面试阿里的时候一定会问到的

android 程序员 移动开发

推荐!DevOps工具正越来越自动化

SoFlu软件机器人

新版本发布!openLooKeng v1.4.0上线

openLooKeng

大数据 计算引擎 openLooKeng

实用推荐系统:寻找有用的用户行为

博文视点Broadview

飞鹤乳业数智化转型之路

大咖说

云计算 数字化转型 数字化 企业上云

TDSQL已助力20余家金融机构完成核心替换

腾讯云数据库

tdsql 国产数据库

2020 更新 - 腾讯 Android 面试 (已拿到月薪22K offer)

android 程序员 移动开发

Hazelcast在openLooKeng中的应用(Cache篇)

openLooKeng

大数据 cache 计算引擎 openLooKeng

基于Hive Connector的openLooKeng Connector 创建复用机制剖析

openLooKeng

大数据 hive 多数据源配置 计算引擎 openLooKeng

性能优化反思:不要在for循环中操作DB

CRMEB

浅析openLooKeng安全认证机制

openLooKeng

大数据 ldap openLooKeng 安全认证

使用JPA + Eclipselink操作PostgreSQL数据库

汪子熙

eclipse 数据库 11月日更

硬核干货!TDSQL全局一致性读技术详解|

腾讯云数据库

tdsql 国产数据库

JPA + EclipseLink + 云平台 = 运行在云端的数据库应用

汪子熙

数据库 Cloud Cloud Native 11月日更

教你如何在Spark Scala/Java应用中调用Python脚本

华为云开发者联盟

Java Python spark JVM Spark Scala

TDSQL | 深度解读HTAP系统的问题与主义之争

腾讯云数据库

tdsql 国产数据库

超简单教程!自动部署openLooKeng

openLooKeng

大数据 计算引擎 openLooKeng 安装部署

2020Android-目前最稳定和高效的UI适配方案!你头秃都没想到还能这样吧!

android 程序员 移动开发

2020Android面经,历时一个半月,斩获3个大厂offer,移动端开发工程师面试题

android 程序员 移动开发

助力邯钢工业4.0!TDengine在深度(平潭)节水减排项目中的应用

TDengine

数据库 tdengine 后端

用一套代码实现APP和小程序

Speedoooo

容器 移动开发 ios开发 APP开发 Andriod开发

2020 国内互联网公司的Android工程师薪酬排名!看看你是什么水平

android 程序员 移动开发

2020Android开发者学习路线(快速篇),分析android进程管理机制

android 程序员 移动开发

【Java原理剖析系列】深度synchronized工作原理分析

洛神灬殇

java 11月日更

恒源云(GPUSHARE)_U1S1,1年1度GPU云种草大会

恒源云

深度学习

模块三作业

doublechun

「架构实战营」

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