你还以为StringBuffer是线程安全?别天真了。
每一个学过java的小伙伴都会背,StringBuffer是线程安全的,StringBuilder是非线程安全的;Hashtable是线程安全的,HashMap是非线程安全的。把这几条当成公理在用了,我面试的同学中,不管能力好坏,这几句都能背出来。
我们看一下StringBuffer的官方注释:
就连官方的注释上也写着,StringBuffer
是一个线程安全的可变的字符序列。StringBuffer可以安全的在多线程场景下使用。
事实真的是这样的吗?还真不是。StringBuffer既不是线程安全
的,而且是一点卵用没有,任何出现StringBuffer
的地方都可以用StringBuilder
去替换。
为什么StringBuffer不是线程安全的
首先咱们得定义什么是线程安全
,线程安全
就是在多线程运行的环境下,最终输出结果是正确的。其实任何一个类,即便它的所有方法都是synchonized
,你也不能无中生有、暗度陈仓、凭空想象、胡作非为。
咱们看一下StringBuffer
的常用方法
通常我们用的比较多的是append
、insert
、substring
这些方法。你好好想一下,这些方法如果在多线程环境运行的情况下,它能保证程序运行结果的正确性和一致性吗?
•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
终于在jdk9,把内部代码中用到的StringBuffer给干掉了。我们来做个实验验证一一。
写个简单的代码
然后通过jdk自带的jcmd
工具,分别针对Java 8 Update 102
、Java 8 Update 121
、OpenJDK 9.0 ea+164
三个版本进行测试,结果如下:
可以看到,前面两个java8的版本,分别有30个StringBuffer
实例,而最后的java9的版本,在运行前面的示例程序时,是没有创建任何StringBuffer
实例的。
也希望大家能把自己的项目中的StringBuffer清理一下,希望StringBuffer
能在下一个10年彻底消失掉。
后记
今年接手的一个项目,在执行sonar检查时,问题最多的就是不应该使用StringBuffer,而应该使用STringBuilder
,足足有将近2000个。这可怎么改,一天改200个,还得改10天。当我分析出一个结论,就是就没有在多线程情况下使用StringBuffer的场景,那我就一不做二不休,直接全局替换(但是要悄悄的,不能让测试同学知道了),10分钟替换完,20分钟编译通过,搞定!
本文分享自微信公众号 - 程序员阿水(gh_124d28263603)。
如有侵权,请联系 [email protected] 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。