10 月 23 - 25 日,QCon 上海站即将召开,现在购票,享9折优惠 了解详情
写点什么

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

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

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

关注

评论

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

Python代码阅读(第63篇):数字奇偶性

Felix

Python 编程 Code 阅读代码 Python初学者

回顾我两个月面试阿里,携程,小红书,美团,网易等等(Java岗)

热爱java的分享家

Java 架构 面试 程序人生 经验分享

SAP ERP classification 和 SAP Cloud for Customer 的同步

汪子熙

中间件 SAP ERP C4C 11月日更

字节跳动如何系统性治理 iOS 稳定性问题

字节跳动终端技术

ios 字节跳动 APM APP稳定性

TDengine在理想汽车物联网业务场景的落地实践

TDengine

tdengine 时序数据库

闭包与内存泄露

Jeannette

async/await 优雅永不过时

CRMEB

【Pandas学习笔记01】强大的分析结构化数据的工具集

恒生LIGHT云社区

Python 大数据 数据分析 pandas

恒源云(GPUSHARE)_训练一个专门捣乱的模型

恒源云

人工智能 深度学习 算力

SAP 公有云和私有云解决方案概述

汪子熙

公有云 云平台 SAP 11月日更 公有云私有云

JS柯里化和反柯里化

Jeannette

应急响应入门之Linux分析排查

网络安全学海

网络安全 信息安全 渗透测试 WEB安全 安全漏洞

两个小时手写了个Zookeeper分布式服务注册中心

热爱java的分享家

Java 面试 程序人生 编程语言 经验分享

Javascript的内存管理

Jeannette

跨端分布式计算技术初探|HDC2021技术分论坛

HarmonyOS开发者

HarmonyOS

阿里蚂蚁花呗团队面试题:spring+分布式+jvm+session+redis

热爱java的分享家

Java 面试 程序人生 编程语言 经验分享

下单延迟10s撤单性能测试

FunTester

性能测试 延迟队列 接口测试 测试框架 FunTester

先到先得!Alibaba甩出第四次更新的JDK源码高级笔记(终极版)

热爱java的分享家

Java 源码 jdk 面试 经验分享

JS函数的this

Jeannette

架构实战营 模块五

felix

#架构实战营

一周信创舆情观察(11.15~11.21)

统小信uos

分布式软时钟有多重要?|HDC2021技术分论坛

HarmonyOS开发者

HarmonyOS

硬科技起飞,这家成立仅三年的AI研究院已颇具国际风范

硬科技星球

尝试 Promise A+

Jeannette

广发证券携手HarmonyOS打造智慧金融服务|HDC2021技术分论坛

HarmonyOS开发者

HarmonyOS

元宇宙最缺的是内容,而云计算能帮上忙

百度开发者中心

云计算 元宇宙

行业分析| 大势所趋:元宇宙

anyRTC开发者

音视频 视频通话 虚拟现实 元宇宙

CSS布局(五)之圣杯布局和双飞翼布局

Augus

CSS 11月日更

智慧园区一体化管理平台开发,园区智能化集成系统

电微13828808271

微信 ClickHouse 实时数仓的最佳实践

科技热闻

2021年26家大厂Java面试题整理了360道(分布式+微服务+高并发)

热爱java的分享家

Java 架构 面试 程序人生 经验分享

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