写点什么

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

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

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

关注

评论

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

ARTS打卡 第10周

引花眠

ARTS 打卡计划

秒杀系统问题与方案设计

superman

秒杀 架构总结

什么?不写代码也能做功能开发! -RUOYI 教程二

Java_若依框架教程

Java 无代码开发 若依

数据库系统设计概述

码哥字节

数据库 redis mongodb elasticsearch 数据库设计

架构师训练营第八章-作业1

A Matt

飞天茅台超卖事故:Redis分布式锁请慎用!

程序员生活志

redis 分布式

系统设计系列之如何设计一个短链服务

看山

架构 面试 分布式 架构设计 短链服务

Vue中使用装饰器,我是认真的

前端有的玩

Java Vue 装饰器

云小课 | IPv4枯了,IPv6来了

华为云开发者联盟

IP 公有云 虚拟私有云 华为云 虚拟化

财务分析与主要的模型

金松(李博源)

你问我答:微服务治理应该如何去做?

BoCloud博云

容器 微服务 PaaS API 博云

华为云GaussDB(DWS)内存知识点,你知道吗?

华为云开发者联盟

数据库 大数据 数据 内存 华为云

Newbe.Claptrap 框架如何实现多级生命周期控制?

newbe36524

架构 微服务 .net core ASP.NET Core

计算机网络基础(十一)---网络层-OSPF协议

书旅

计算机网络 网络 协议栈 OSPF

智能膜切机,解决手机贴膜行业难题

Geek_116789

数据人必须知道的SQL概念(A—Z)

大唐小生

sql 数据 职场成长

微软苏州集体抵制来自阿里、华为的跳槽者:请停止你的“奋斗逼”行为!网友:看到 955 不加班的公司名单,我酸了

程序员生活志

程序员 加班 996

在人工智能时代追逐的“后浪”

华为云开发者联盟

程序员 AI 开发者 技术社区 华为云

初识分布式:MIT 6.284系列(一)

Kerwin

分布式 MIT 28天写作

技术管理者带团队的几个实用技巧

Phoenix

团队管理 企业文化 团队 价值观

授人以渔:stm32资料查询技巧

华为云开发者联盟

架构 armv8 芯片 华为云 二进制

实用!一键生成数据库文档,堪称数据库界的Swagger

程序员小富

Java MySQL

《深度工作》学习笔记(3)

石云升

学习 深度工作 工作哲学

<<前端进阶篇>> PDF 出炉了 — 「阿宝哥」,精心准备的 6 万多字 170 页的前端进阶资料

阿宝哥

大前端

JVM系列之:JIT中的Virtual Call

程序那些事

Java JVM JIT

英特尔®AI计算盒参考设计发布 加速智能边缘崛起

最新动态

基于 Golang的侵入式 Opentracing实现全链路追踪 ----实践篇

是老郭啊

第九周

hdhdh

将信将疑,将中台进行到底

郭华

手写一个重入锁

诸葛小猿

synchronized CAS 重入锁 compareAndSwap ReentrantLock

老哥,您看我这篇Java集合,还有机会评优吗?

苹果看辽宁体育

Java 后端

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