燃爆上海 5·23-24,AICon 大模型实战风暴,50+ 干货一网打尽,100% 日程上线 了解详情
写点什么

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

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

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

关注

评论

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

如何提升研发效能实现 10 倍研发效能提升

阿里云云效

云计算 阿里云 云原生 研发效能 研发

毕设:设计电商秒杀系统

王大胖

移动App的设计流程是怎样的?

InfoQ IT百科

Android C++系列:C++最佳实践5 const

轻口味

c++ android ndk jni 4月月更

Hoo虎符研究院 |ETH2.0合并在即 速来围观流动性质押赛道的潜力项目

区块链前沿News

eth 虎符 Hoo 虎符交易所

Java篇-序列化与反序列化

是老郭啊

Java 对象 序列化 反序列化

阿里云 云效一站式研发平台

阿里云云效

阿里云 DevOps 云原生 研发 一站式研发平台

百尺竿头更进一步 – Amazon Aurora 的读写能力扩展之 ShardingSphere-Proxy 篇

亚马逊云科技 (Amazon Web Services)

Tech 专栏

【MARS TALK 05】今日头条 App 基于火山引擎MARS研发流程最佳实践

字节跳动终端技术

android 今日头条 字节跳动 研发

Carina 本地存储入选 CNCF 云原生全景图

BoCloud博云

开源 cncf 本地存储

从杀慢查询入手来预防 MySQL 雪崩的办法

Qunar技术沙龙

dba

怎么做SEO网站优化?

InfoQ IT百科

真香!盘点云主机三种典型应用场景

天翼云开发者社区

云主机

2022鲲鹏开发者创享日即将扬帆起航 与开发者共创未来共享非凡成就

科技热闻

TASKCTL 容器签出失败解决方法

敏捷调度TASKCTL

大数据 DevOps 分布式 ETL 自动化运维

怎么做App分发?

InfoQ IT百科

TASKCTL 作业流程无触发设计

敏捷调度TASKCTL

大数据 DevOps 分布式 自动化部署 ETL任务

如何优雅高效地管理公司文档?

小炮

文档 文档管理

新基建下纵览全局,2+4+31+X实力出镜

天翼云开发者社区

云计算 云技术

容器化应用:我们都经历了什么

Rayzh

Docker Kubernetes 云原生 Cloud Native

深入JVM内置锁 synchronized 底层

janyxe

JVM synchronized synchronized锁升级过程

Carina 的根基与诞生背景|深入了解 Carina 系列 第一期

BoCloud博云

开源 本地存储

Docker 实战教程之从入门到提高 (八)

汪子熙

Docker 容器 docker image 容器镜像 4月月更

App分发是什么意思?

InfoQ IT百科

App怎么做灰度发布?

InfoQ IT百科

不care工具,在大数据平台中Hive能自动处理SQL

华为云开发者联盟

sql 大数据 mapreduce hive 数据分析

一文详解:企业数字化的未来基石——微服务

穿过生命散发芬芳

微服务 4月月更

数据连接一切,开启融合数据云新时代——星环科技春季新品发布周盛大开启

星环科技

App能收集哪些个人信息?

InfoQ IT百科

IM开发技术分享:浅谈IM系统中离线消息、历史消息的最佳实践

JackJiang

网络编程 TCP协议 即时通讯IM im开发

GPU市场规模和各子市场趋势分析

Finovy Cloud

人工智能 gpu

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