写点什么

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

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

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

关注

评论

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

final的两个重排序规则

爱好编程进阶

程序员 后端开发

Backbone 之 DetNet:为检测而生(Pytorch实现及代码解析

爱好编程进阶

Java 程序员 后端开发

CentOS7 部署 LAMP 平台与应用

爱好编程进阶

Java 程序员 后端开发

CPU战争40年,终于把Intel打趴下了

爱好编程进阶

Java 程序员 后端开发

技术揭秘 | 阿里云EMR StarRocks 线上发布会预约开启!

阿里云大数据AI技术

StarRocks 产品发布会

43岁老程序员的编程之路,我是如何做到退休的?龙叔真的退休了吗

爱好编程进阶

Java 程序员 后端开发

2021字节、阿里大厂高频面试真题1000道(附答案解析

爱好编程进阶

Java 程序员 后端开发

2022年Java面试题最新整理,附白话答案

爱好编程进阶

Java 程序员 后端开发

5年从初学者变成行业大拿,月薪暴涨10倍,我的经验值得借鉴

爱好编程进阶

程序员 后端开发

6年,终拿腾讯 offer!

爱好编程进阶

程序员 后端开发

Java 基础语法

源字节1号

软件开发 前端开发 Java后端 小程序开发

2020最后一次Java面试,快手三面一轮游,如今已拿意向书

爱好编程进阶

Java 程序员 后端开发

Dubbo中的统一契约是如何实现的?

爱好编程进阶

Java 程序员 后端开发

Eclipse+Java+Swing实现企业人事管理系统

爱好编程进阶

Java 程序员 后端开发

如何将知识管理应用到工作中,解决企业的问题?

小炮

Cloud-借助消息队列解决分布式事务

爱好编程进阶

Java 程序员

Dubbo

爱好编程进阶

程序员 后端开发

Eclipse+Java+Swing实现仓库管理系统

爱好编程进阶

Java 程序员 后端开发

图片

武师叔

CentOS安装MySQL详解

爱好编程进阶

Java 程序员 后端开发

Day461

爱好编程进阶

程序员 后端开发

CRMEB Java.小程序交易组件操作使用教程

CRMEB

银丰新融:搭建名单监控管理系统,落实“三反”政策

华为云开发者联盟

安全 GaussDB 反洗钱 名单监控管理系统

B站【狂神说Java笔记】-java基础语法

爱好编程进阶

Java 程序员 后端开发

CCF201712-2 游戏

爱好编程进阶

Java 程序员 后端开发

Day269

爱好编程进阶

Java 程序员 后端开发

java培训分布式和集群的区别

@零度

分布式 JAVA开发 集群

Bootstrap表格

爱好编程进阶

Java 程序员

ElasticSearch三节点集群搭建笔记(中心化版本)

爱好编程进阶

程序员 后端开发

09-SSO微服务工程中用户行为日志的记录(2107~2108~2109

爱好编程进阶

Java 程序员 后端开发

全球最大的半导体IP产业链,你了解多少?

Finovy Cloud

gpu GPU服务器 显卡、gpu

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