今天分享一个题目:
为什么StringBuilder是线程不安全的?
虽然看起来和日常的Buffer题很像, 但这题目非常邪门.
为什么StringBuffer是线程安全的?
Buffer
的问法, 只需要回答加锁了就行. 但是Builder
这问法,难道要回没加锁?这不成了左右↔横跳了.
S&B 一点历史
StringBuffer出现在Java1.0时代.
按照我们的经验, 那个时代就出现的东西, 基本上都是比较幼稚的,现在大多被改过或废弃了.比如:
-
Integer
,Long
等包装类的构造器 -
Thread
的stop
,suspend
,resume
-
Object
的finalize
-
Vector,Stack
,HashTable
,Enumeration
他们被Java1.2出现的Collections
取代了. -
synchronized
的初代目, Java5的JUC包提供了大量更高级的功能, Java1.6时代的synchronized大幅度优化了. -
java.util.Date
这个类里有几十个@Deprecated
方法. 现在都是用Java8
的time包了. -
….
过度设计
StringBuffer
也是类似的,他的问题是,过度设计.
如果打开源码看, 会发现StringBuffer
里到处都是synchronized
关键字, 然而大部分时候, 人们其实是用不到StringBuffer的线程安全特性的.这里有一个过度设计存在, 需要做减法.
StringBuilder
StringBuilder出现在Java1.5
. 接口上,二者完全兼容,一个全局的替换跑下来, 基本上就可以整体升级掉了.
从最初的设计目的上来说,StringBuilder
-
为了性能做了妥协版本的StringBuffer -
针对单线程场景做了优化的StringBuffer -
就是放弃了线程安全版本的StringBuffer
StringBuilder从设计目的上来说, 就是放弃了线程安全的.
所以, 问一个放弃了安全措施的东西为什么是不安全的, 根本无从回答. 这个东西的离谱程度,相当于下面的问题.
-
为什么竹篮打水是会一场空的? -
为什么穿内衣上街是会被当成变态的(超人除外)? -
为什么五菱mini不能上高速? -
….
这答案是理所当然的, 因为他压根不是为那个场景设计的.
前几天,我们介绍Optional时候,曾经引用过这么一段话,放到这里来,一样好用.
The best way to use things is to exploit them for what they have been created and tested for in the first place. jkl,公众号:K字的研究Null从哪里来?
线程安全的本质
多线程对比多进程的特点, 就是共享了内存, 降低了内存占用. 理所当然的, 也面临了共享内存带来的不确定性问题, 这个既是共享内存编程模型
的优点,也是他的缺点.线程安全并不是一件触手可及的事, 任何没有声称过自己是线程安全的类型, 都应该视作不安全的,今天的题目根本就不应该问.
不过今天都说到这了,还是谈谈为什么StringBuilder是不安全的吧.前阵子在介绍算法导购第27章的书评里摘抄了这么一段话:
一个多线程算法是确定的( deterministic),如果在同样的输人情况下总是做相同的事,且无论指令在多核计算机上如何被调度也是如此。一个多线程算法是非确定的( nondeterministic),如果每次执行它做的事情有所不同。一个多线程算法意图确定地做一些事情,但常常会失败,究其原因是算法中包含了“确定性竞争” jkl,公众号:K字的研究推荐一下算法导论第27章多线程算法
线程安全问题,本质上是确定性竞争
的问题. StringBuilder的append操作是这样的.
if sb refers to an instance of a StringBuilder, then sb.append(x) has the same effect as sb.insert(sb.length(), x). java.lang.StringBuilder
这里面其实有4步:
-
获取sb长度
-
获取str长度
-
确保容量
-
存放内容
这些步骤放在一起, 最后运行结果有极大不确定性.
-
第一步完全依赖于别的线程进行的次序, 这时候会乱序.
-
第三步则可能出现a线程扩容,b线程发现容量够就直接写入的情况.这时候就会丢数据的情况.
如果你和我一样, 不擅长写多线程内容,那么请牢记以下3条法则:
-
第一最好不共享 -
第二最好不可变 -
第三最好多上B站看UP主绿导师原谅你了 的课.
最后,报告一个好消息
去年, 介绍算法导论第27章时候聊过, 《算法导论》第四版快发售了。
现在看消息, 好像4月份就要上市了,泄漏出来的插图很不错,我喜欢.


最后再总结一下, 线程安全的终极解决方案: 不共享>>不可变>>加锁.
魏文侯曰:子昆弟三人其孰最善为医?
扁鹊曰:长兄最善,中兄次之,扁鹊最为下。
魏文侯曰:可得闻邪?
扁鹊曰:长兄于病视神,未有形而除之,故名不出于家。中兄治病,其在毫毛,故名不出于闾。若扁鹊者,镵血脉,投毒药,副肌肤,闲而名出闻于诸侯鶡冠子·卷下·世贤第十六
图文无关,题图是瞎选的,我也不知道配什么图好.
原文始发于微信公众号(K字的研究):为什么StringBuilder是线程不安全的?
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/24799.html