你还以为StringBuffer是线程安全?别天真了。

每一个学过java的小伙伴都会背,StringBuffer是线程安全的,StringBuilder是非线程安全的;Hashtable是线程安全的,HashMap是非线程安全的。把这几条当成公理在用了,我面试的同学中,不管能力好坏,这几句都能背出来。

我们看一下StringBuffer的官方注释:

就连官方的注释上也写着,StringBuffer是一个线程安全的可变的字符序列。StringBuffer可以安全的在多线程场景下使用。

事实真的是这样的吗?还真不是。StringBuffer既不是线程安全的,而且是一点卵用没有,任何出现StringBuffer的地方都可以用StringBuilder去替换。

为什么StringBuffer不是线程安全的

首先咱们得定义什么是线程安全,线程安全就是在多线程运行的环境下,最终输出结果是正确的。其实任何一个类,即便它的所有方法都是synchonized,你也不能无中生有、暗度陈仓、凭空想象、胡作非为。

咱们看一下StringBuffer的常用方法

你还以为StringBuffer是线程安全?别天真了。-LMLPHP

通常我们用的比较多的是appendinsertsubstring这些方法。你好好想一下,这些方法如果在多线程环境运行的情况下,它能保证程序运行结果的正确性和一致性吗?

append为例从参加工作到现在,我遇到的所有append,拼接sql是多较多的,或者是把数据库中的几个字段拼接成一段话。如果是多线程环境运行,你根本无法预测最终结果是什么,不光是你预测不了,JVM自己都不知道最终出来的是个什么货,只能交给天意了以insert为例如果你要insert, 你需要知道自己是要insert到哪一个位置,比如在第一个出现的媳妇前插入一句我爱你三个字,那你写代码的话就是两行代码

同志们,发现啥问题没,你要完成这个功能需要三步操作,当你完成第一步操作算出index是多少的时候,这时候很可能出现一个不怀好意的第三者线程从中作梗,最后你发现输出的结果根本不是那么回事。如果你想要这个功能好使,你还是得自己弄把锁,把刚才的方法锁住,确保你的操作是原子性的,其他要操作这个stringBuffer的地方,得拿到这把锁才行。


说了这么多,你发现了没,你找不到一个用StringBuffer的理由,我工作这么久是没见过,不光我没见过,Effective java的作者josh bloch也说没见过,他在书中说:

既然无用,那就让我们来消灭StringBuffer吧

java 5.0在2006年发布时,提供了StringBuilder这个类,到现在14年已经过去了。这个StringBuffer还有屹立不倒的出现在各种代码中。就连java的源码中,也到处充斥着无用的StringBuffer

终于在2014年的某一天,一名叫Paul Sandoz的人实在受不了了,于是给openjdk提了个issue,说咱能不能把java内部核心库中用到StringBuffer的地方替换为StringBuilder

你还以为StringBuffer是线程安全?别天真了。-LMLPHP

终于在jdk9,把内部代码中用到的StringBuffer给干掉了。我们来做个实验验证一一。

写个简单的代码

然后通过jdk自带的jcmd工具,分别针对Java 8 Update 102Java 8 Update 121OpenJDK 9.0 ea+164三个版本进行测试,结果如下:

你还以为StringBuffer是线程安全?别天真了。-LMLPHP

可以看到,前面两个java8的版本,分别有30个StringBuffer实例,而最后的java9的版本,在运行前面的示例程序时,是没有创建任何StringBuffer实例的。

也希望大家能把自己的项目中的StringBuffer清理一下,希望StringBuffer能在下一个10年彻底消失掉。

后记

今年接手的一个项目,在执行sonar检查时,问题最多的就是不应该使用StringBuffer,而应该使用STringBuilder,足足有将近2000个。这可怎么改,一天改200个,还得改10天。当我分析出一个结论,就是就没有在多线程情况下使用StringBuffer的场景,那我就一不做二不休,直接全局替换(但是要悄悄的,不能让测试同学知道了),10分钟替换完,20分钟编译通过,搞定!


本文分享自微信公众号 - 程序员阿水(gh_124d28263603)。
如有侵权,请联系 [email protected] 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

09-08 06:13