2025上半年,最新 AI实践都在这!20+ 应用案例,任听一场议题就值回票价 了解详情
写点什么

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

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

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

关注

评论

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

Apache ShardingSphere 在京东白条场景的落地之旅

SphereEx

开源 数据架构 架构设计 ShardingSphere SphereEx

211本+985硕+计算机专业投面百度,坐等一周迎来三面,已拿offer

Java 学习 程序员 架构 大厂面试

亿级流量架构演进实战 | 从零构建亿级流量API网关 01

松然聊技术

亿级架构

DataOps(数据运维)指南 - 数据管理的新时代

码语者

DataOps

百亿级系统架构首公开!阿里这份300多页的设计实录你还没有吗?

Java 程序员 架构 面试 后端

性能加速最高可达28倍!这个NLP工具包不容错过

百度开发者中心

预训练模型 NLP 大模型

Elasticsearch 快照相关(qbit)

qbit

主数据与主数据管理(数据治理)

KoLee

数据治理 数字化 主数据管理 主数据

涨薪60%,从美团干到阿里p7,这份Github上的面试笔记把所有Java知识都写出来了

Java 程序员 架构 面试 后端

云栖大会|盛宴之下,共赴一场视频云的进化论

阿里云CloudImagine

阿里云 音视频 WebRTC 视频云 云栖大会

架构师一定要看!微服务设计的四个原则

Java 程序员 架构 面试 后端

三面阿里,有惊无险成功拿到offer定级P7,只能说是真的难

Java 编程 java架构

量化模拟线上流量实践

FunTester

性能测试 接口测试 测试框架 FunTester 线上流量

阿里技术官耗时半年总结出“满分”架构笔记,拿捏分布式到微服务

进击的王小二

Java 架构 分布式 微服务

数据上报那些事

神策技术社区

数据 神策数据

容器化 | ClickHouse Operator 原理解析

RadonDB

数据库 Kubernetes Clickhouse

太绝了吧! 终于有人能把TCP/IP 协议讲的明明白白了

程序员 架构 面试 后端 java

亿级流量架构演进实战 | 从零构建亿级流量API网关 02

松然聊技术

亿级架构

优酷鸿蒙开发实践 | 鸿蒙卡片开发

阿里巴巴终端技术

ios android HarmonyOS 优酷 移动端

元宇宙NFT区块链游戏系统开发

Python 的 sum():Pythonic 的求和方法

华为云开发者联盟

Python 列表 元组 Pythonic 求和

遭 GitHub 连夜封杀下架?被泄露的阿里内部 Java 面试手册到底有多强?

收到请回复

Java 面试 阿里 大厂Offer

接连三次霸榜GitHub,这个国产GitHub项目是真的强...

百度开发者中心

最佳实践 方法论 百度飞桨 开源技术

解读鸿蒙轻内核的监控器:异常钩子函数

华为云开发者联盟

鸿蒙 钩子函数 任务栈 OpenHarmony 异常钩子函数

分布式缓存技术

黄敏

Python代码阅读(第37篇):获取两个列表中相同的元素

Felix

Python 编程 Code Programing 阅读代码

Android技术分享| 【自习室】自定义View代替通知动画(1)

anyRTC开发者

android 音视频 WebRTC 在线教育 移动开发

Alibaba最新神作!耗时182天肝出来的1015页分布式全栈手册太香了

编程 程序员 IT 计算机 java

2021Flexera云报告:企业积极拥抱多云,但云上成本仍然居高不下

行云管家

区块链 云计算 企业上云 上云

面试官提问:如何通过sql方式将数据库表行转列?

Java 数据库 sql 面试 后端

第 17 章 -《Linux 一学就会》- Linux计划任务与日志的管理

学神来啦

Linux 运维 linux学习 linux云计算 linux基础

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