你还以为StringBuffer是线程安全?别天真了。
你还以为StringBuffer是线程安全?别天真了。
每一个学过java的小伙伴都会背,StringBuffer是线程安全的,StringBuilder是非线程安全的;Hashtable是线程安全的,HashMap是非线程安全的。把这几条当成公理在用了,我面试的同学中,不管能力好坏,这几句都能背出来。
我们看一下StringBuffer的官方注释:
StringBuffer is A thread-safe, mutable sequence of characters. A string buffer is like a String, but can be modified. At any point in time it contains some particular sequence of characters, but the length and content of the sequence can be changed through certain method calls. String buffers are safe for use by multiple threads. The methods are synchronized where necessary so that all the operations on any particular instance behave as if they occur in some serial order that is consistent with the order of the method calls made by each of the individual threads involved.
就连官方的注释上也写着,StringBuffer
是一个线程安全的可变的字符序列。StringBuffer可以安全的在多线程场景下使用。
事实真的是这样的吗?还真不是。StringBuffer既不是线程安全
的,而且是一点卵用没有,任何出现StringBuffer
的地方都可以用StringBuilder
去替换。
为什么StringBuffer不是线程安全的
首先咱们得定义什么是线程安全
,线程安全
就是在多线程运行的环境下,最终输出结果是正确的。其实任何一个类,即便它的所有方法都是synchonized
,你也不能无中生有、暗度陈仓、凭空想象、胡作非为。
咱们看一下StringBuffer
的常用方法
通常我们用的比较多的是append
、insert
、substring
这些方法。你好好想一下,这些方法如果在多线程环境运行的情况下,它能保证程序运行结果的正确性和一致性吗?
•append为例•从参加工作到现在,我遇到的所有append,拼接sql是多较多的,或者是把数据库中的几个字段拼接成一段话。•如果是多线程环境运行,你根本无法预测最终结果是什么,不光是你预测不了,JVM自己都不知道最终出来的是个什么货,只能交给天意了•以insert为例•如果你要insert, 你需要知道自己是要insert到哪一个位置,比如在第一个出现的媳妇
前插入一句我爱你
三个字,那你写代码的话就是两行代码
int index = stringBuffer.indexOf(“媳妇”);
if(index >= 0){
stringBuffer.insert(index,“我爱你”);
}
同志们,发现啥问题没,你要完成这个功能需要三步操作,当你完成第一步操作算出index是多少的时候,这时候很可能出现一个不怀好意的第三者线程从中作梗,最后你发现输出的结果根本不是那么回事。如果你想要这个功能好使,你还是得自己弄把锁,把刚才的方法锁住,确保你的操作是原子性的,其他要操作这个stringBuffer的地方,得拿到这把锁才行。
说了这么多,你发现了没,你找不到一个用StringBuffer的理由,我工作这么久是没见过,不光我没见过,Effective java
的作者josh bloch
也说没见过,他在书中说:
StringBuffer instances are almost always used by a single thread, yet they perform internal synchronization. It is for this reason that StringBuffer was supplanted by StringBuilder, which is just an unsynchronized StringBuffer.
既然无用,那就让我们来消灭StringBuffer吧
java 5.0在2006年发布时,提供了StringBuilder
这个类,到现在14年已经过去了。这个StringBuffer
还有屹立不倒的出现在各种代码中。就连java的源码中,也到处充斥着无用的StringBuffer
。
终于在2014年的某一天,一名叫Paul Sandoz
的人实在受不了了,于是给openjdk提了个issue,说咱能不能把java内部核心库中用到StringBuffer
的地方替换为StringBuilder
终于在jdk9,把内部代码中用到的StringBuffer给干掉了。我们来做个实验验证一一。
写个简单的代码
public class Main {
public static void main(final String[] args) throws IOException {
System.out.println("Waiting [press ENTER to exit] ..");
System.in.read();
}
}
然后通过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)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
如何写好QSPI驱动
1. 摘要 本篇笔记主要介绍,如何开发稳定可靠,功能齐全的QSPI驱动。 2. 准备工作 1, IAR 8.32.1 2, STM32Cube_FW_H7_V1.6.0 3. QSPI简介 4. QSPI驱动 在项目的开发中,我们经常会使用外挂Flash在做一些应用,而STM32H743带QSPI接口,可以用来外挂QSPI Flash,在之前的推送中我们以winbond华邦的W25Q256为例给大家说明了下QSPI的操作,借助ST的HAL库例程可以快速验证,不同型号的QSPI Flash会有一些参数和命令区别,大家应用时候要注意,例如美光和华邦的有的命令码就不同。ST的驱动里STM32H743的评估板是以美光的MT25T_QLKT_L_01G为例。如果你使用不同的型号,请注意区分,既然是评估板,那么就只是给大家参考,在正式的项目中,还需要考虑一些其他的问题。 5. 驱动架构 驱动的架构可以参考STM32Cube_FW_H7中对QSPI Flash驱动架构,也可以自己做架构,尽量简单明了,易用且稳定。对具体型号Flash的驱动可以放在一个文件下建立一个.c和.h. 如我们以华邦的...
- 下一篇
ddns-go 0.0.7 发布,简单好用的 DDNS 动态域名服务工具
这是一个简单好用的DDNS动态域名服务工具,可以自动更新域名解析到公网IP,目前支持Alidns(阿里云)、Dnspod(腾讯云)、Cloudflare v0.0.7修改 新增功能:填写ID/Secret后,隐藏真实的ID/Secret 修复:阿里云直接解析主域名 页面:增加token的链接地址 功能 自动获得你的公网IPV4或IPV6并解析到域名中 支持Mac、Windows、Linux系统,支持ARM、x86架构 间隔5分钟同步一次 支持多个域名同时解析 支持多级域名 支持的域名服务商Alidns(阿里云)Dnspod(腾讯云)Cloudflare 系统中使用 下载https://github.com/jeessy2/ddns-go/releases 双击运行,程序自动打开http://127.0.0.1:9876,修改你的配置,成功 Docker中使用 docker run -d \ --name ddns-go \ --restart=always \ -p 127.0.0.1:9876:9876 \ jeessy/ddns-go 在docker主机上打开http:...
相关文章
文章评论
共有0条评论来说两句吧...