写点什么

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

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

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

关注

评论

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

万物互联,车联网数字化需要快速引入小程序生态

Speedoooo

小程序 车联网 IoT 小程序生态

智为链接,慧享生活,荣耀智慧服务,只为 “懂” 你

荣耀开发者服务平台

开发者 安卓 荣耀 honor

Colocate Join :ClickHouse的一种高性能分布式join查询模型

华为云开发者联盟

数据库 后端

MySQL 原理与优化:Update 优化

老崔说架构

“低代码”编程或将是软件开发的未来

优秀

低代码

产品说明丨如何使用MobPush快速创建应用

MobTech袤博科技

ios android sdk mobpush 智能推送

网络安全——XSS之被我们忽视的Cookie

Jack20

网络安全

易观分析联合中小银行联盟发布海南数字经济指数,敬请期待!

易观分析

金融 海南数字经济 易观分析

fastposter v2.9.1 程序员必备海报生成器

物有本末

海报生成器 海报编辑器 图片生成 二维码生成

易观千帆银行用户体验中心:聚焦银行APP用户体验

易观分析

金融 银行 用户体验

头脑风暴:目标和

HelloWorld杰少

数据结构 算法 LeetCode 8月月更

NFT数字藏品——数字藏品发行平台开发

开源直播系统源码

软件开发 数字藏品 数字藏品系统软件开发

避免 10 大 NGINX 配置错误(上)

NGINX开源社区

nginx 架构 配置 配置分析 故障排除

一文带你了解 HONOR Connect

荣耀开发者服务平台

开发者 教程 荣耀 honor

数据产品经理那点事儿 一

金松(李博源)

数据产品经理

leetcode 739. Daily Temperatures 每日温度(中等)

okokabcd

LeetCode 数据结构与算法 栈和队列

缺少比较器,运放来救场!(运放当做比较器电路记录)

矜辰所致

电路设计 8月月更 比较器 运放

符合信创要求的堡垒机有哪些?支持哪些系统?

行云管家

信创 堡垒机 信创产业

兆骑科创高层次人才创业大赛平台,投融资对接,双创服务

兆骑科创凤阁

用好 DIV 和 API,在前端系统中轻松嵌入数据分析模块

葡萄城技术团队

前端 嵌入式 BI 可视化数据

社区动态——恭喜海豚调度中国区用户组新晋 9 枚“社群管理员”

白鲸开源

友邦人寿可观测体系设计与落地

阿里巴巴云原生

阿里云 云原生 可观测 合作案例 友邦人寿

借数据智能,亚马逊云科技助力企业打造品牌内生增长力

Lily

等保2.0一个中心三重防护指的是什么?如何理解?

行云管家

等保 等级保护 等保2.0 安全等级保护

mysql进阶(三十三)MySQL数据表添加字段

No Silver Bullet

MySQL 8月月更 添加字段

兆骑科创创业赛事活动发布平台,创业赛事,项目路演

兆骑科创凤阁

在本地利用虚拟机快速搭建一个小型Hadoop大数据平台

Jack20

云计算 大数据

2022年中国软饮料市场洞察

易观分析

软饮料 市场分析

容器化 | 在 S3 实现定时备份

RadonDB

MySQL 数据库 Kubernetes

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