写点什么

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

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

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

关注

评论

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

Java 面试的“完美圣经”,有了这些还愁面试吗?

Java 程序员 架构 面试 后端

实时音频抗弱网技术揭秘

百度开发者中心

最佳实践 经验分享 智能视频

[架构实战营]模块九作业

xyu

#架构实战营

区块链通证经济的意义

CECBC

我用 10000 张图片合成我们美好的瞬间

荣顶

JavaScript 大前端 canvas 图形处理

AUTOSAR基础篇之OS(上)

SOA开发者

嵌入式软件时序(1)— C语言是怎么编译出来的

SOA开发者

观测云产品更新|新增主机网络性能监测、图表矩形树图、多监测关联查询等功能

观测云

功能更新

还在苦恼网络协议?阿里大佬这份笔记带你从入门到精通!

Java 架构 面试 程序人生 编程语言

RUOYI 框架教程 15|若依框架中 Mysql 操作 | 日期处理

Java_若依框架教程

Java 技术 Ruoyi 框架 若依

gitee上提交PR和issue流程和注意事项

Geek_6cdeb6

机器学习 深度学习 git

一个约定让全球数万AI爱好者相聚,它是如何做到的?

硬科技星球

为了让你搞定数据库选型,这些工程师重写了 26 万行代码

SphereEx

数据库 架构 架构设计 ShardingSphere SphereEx

声网 2020 实时大会后的弱网对抗实践

声网

音视频 网络环境 视频编解码 弱网下的极限实时视频通信

10月活动推荐:2021上汽集团“新四化”技术高峰论坛

SOA开发者

OpenCV学习(三):三重境界

轻口味

OpenCV图像处理 10月月更

区块链通证经济和传统经济的区别,如何实现

CECBC

基于HarmonyOS分布式技术,他们让绘画体验更为出色

Geek_283163

鸿蒙

想提高运维效率,那就把MySQL数据库部署到Kubernetes 集群中

华为云开发者联盟

MySQL 运维 测试 MySQL数据库 Kubernetes 集群

万字长文,一篇吃透WebSocket:概念、原理、易错常识、动手实践

JackJiang

websocket 即时通讯 IM

The Data Way Vol.5|这里有一场资本与开源的 battle

SphereEx

开源 播客 ShardingSphere SphereEx

MongoDB中文社区 Freetalk,一起来玩快闪!

MongoDB中文社区

mongodb

车云一体的应用价值

SOA开发者

RUOYI 框架教程 16|关于若依RuoYi.jar卡顿,僵死,假死,系统无反映解决方案

Java_若依框架教程

技术 Ruoyi 开发 框架 若依

用21张图,把Git 工作原理彻底说清楚

git 架构 面试 后端

明道云当选“中国电子商会数据资源服务创新专业委员会”理事单位

明道云

ShardingSphere X Google 编程之夏:同学,开源你怎么看?

SphereEx

开源社区 ShardingSphere 谷歌 编程之夏

解读业界5种主流的深度网络模型

华为云开发者联盟

模型 网络模型 模型优化 模型量化 深度网络

Docgeni 1.1.0 正式发布!

PingCode研发中心

标签 Docgeni 文档目录 进度展示 日志展示

Python代码阅读(第35篇):完全(深度)展开嵌套列表

Felix

Python 编程 Code Programing 阅读代码

Rtmp Message 与 Chunk格式

webrtc developer

RTMP

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