【AICon】AI 基础设施、LLM运维、大模型训练与推理,一场会议,全方位涵盖! >>> 了解详情
写点什么

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

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

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

关注

评论

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

C++grpc 服务器接收到请求后如何处理

linux大本营

gRPC 序列化 protobuf C++

华为云数据灾备,助力企业应对信息安全

平平无奇爱好科技

华为混合云数据灾备方案,保护企业数据安全

平平无奇爱好科技

如何守好企业数据安全防线?华为云数据灾备告诉您!

平平无奇爱好科技

Go 语言中的 Slice 陷阱:如何避免常见错误

陈明勇

Go golang 切片 三周年连更 切片陷阱

华为云数据灾备,为企业数据安全保驾护航

平平无奇爱好科技

Unity 之 音频类型和编码格式介绍

陈言必行

三周年连更

强大的音频分析编辑工具:Amadeus Pro 汉化激活版

真大的脸盆

Mac Mac 软件 音频编辑 音频处理工具 编辑音频

创新引领・数创未来 | 数据流通与治理专题论坛交流会顺利召开

郑州埃文科技

设计模式之原型模式和建造者模式

共饮一杯无

设计模式 建造者模式 三周年连更

Spring中事务嵌套这么用一定得注意了!!

JAVA旭阳

Java spring

算法题每日一练:组合总和 Ⅳ

知心宝贝

数据结构 算法 前端 后端 三周年连更

跨平台应用开发进阶(五十三):uni-app 通过webview方式嵌套H5实现图片点击下载

No Silver Bullet

uni-app 跨平台应用开发 三周年连更 web-view

Gradle工程适配为Hvigor工程

坚果

OpenHarmony 三周年连更

ES开发指南|如何快速上手ElasticSearch

浅羽技术

全文检索 搜索 Lucence Elastic Search 三周年连更

基于Java+Dubbo设计的智能公交查询系统

DS小龙哥

三周年连更

Prometheus常用资源监控

乌龟哥哥

三周年连更

JavaSE 和 Java EE 分别是什么

HoneyMoose

华为云数据灾备解决方案为您的数字资产提供多重防护

平平无奇爱好科技

火山引擎云原生数据仓库ByteHouse技术白皮书V1.0 (Ⅴ)

字节跳动数据平台

数据仓库 云原生 白皮书 企业号 4 月 PK 榜

华为云灾备方案,让安全到家

平平无奇爱好科技

ChatGPT安全受质疑 网信办发布生成式人工智能服务管理办法意见稿

郑州埃文科技

AI大模型加速RPAxAI时代到来,谁会是RPA领域的杀手级应用?

王吉伟频道

RPA AI大模型 ChatGPT RPAxAI 企业级RPA

【Linux】iptables之防火墙概述及规则匹配+实例(1)

A-刘晨阳

Linux iptables 防火墙规则 三周年连更

【Python实战】XPath采集数据

BROKEN

三周年连更

SpringBoot之Tomcat与Undertow容器性能对比 | 超级详细,建议收藏

bug菌

tomcat 三周年连更 Undertow

面试必考: 手撕代码系列(一)

控心つcrazy

JavaScript 手写代码 前端面试 手撕代码 超全前端面试题

Django笔记十三之select_for_update等选择和更新等相关操作

Hunter熊

Python django select_for_update bulk_create update_or_create

基于Ubuntu安装Kubernetes集群指南

王玉川

Kubernetes 云原生 k8s 安装 集群

Discourse 服务器上手动升级

HoneyMoose

Qz学算法-数据结构篇(链表、栈)

浅辄

数据结构 链表 三周年连更

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