为什么StringBuilder是线程不安全的?

今天分享一个题目:

为什么StringBuilder是线程不安全的?

虽然看起来和日常的Buffer题很像, 但这题目非常邪门.

为什么StringBuffer是线程安全的?

Buffer的问法, 只需要回答加锁了就行. 但是Builder这问法,难道要回没加锁?这不成了左右↔横跳了.

为什么StringBuilder是线程不安全的?

S&B 一点历史

StringBuffer出现在Java1.0时代.

按照我们的经验, 那个时代就出现的东西, 基本上都是比较幼稚的,现在大多被改过或废弃了.比如:

  • Integer,Long等包装类的构造器
  • Threadstop,suspend,resume 
  • Objectfinalize
  • 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步:

  1. 获取sb长度

  2. 获取str长度

  3. 确保容量

  4. 存放内容


这些步骤放在一起, 最后运行结果有极大不确定性.

  • 第一步完全依赖于别的线程进行的次序,  这时候会乱序.

  • 第三步则可能出现a线程扩容,b线程发现容量够就直接写入的情况.这时候就会丢数据的情况.



如果你和我一样, 不擅长写多线程内容,那么请牢记以下3条法则:

  • 第一最好不共享
  • 第二最好不可变
  • 第三最好多上B站看UP主绿导师原谅你了 的课.

最后,报告一个好消息

去年, 介绍算法导论第27章时候聊过, 《算法导论》第四版快发售了。

为什么StringBuilder是线程不安全的?

现在看消息, 好像4月份就要上市了,泄漏出来的插图很不错,我喜欢.

为什么StringBuilder是线程不安全的?

为什么StringBuilder是线程不安全的?
为什么StringBuilder是线程不安全的?



最后再总结一下, 线程安全的终极解决方案: 不共享>>不可变>>加锁.

魏文侯曰:子昆弟三人其孰最善为医?
扁鹊曰:长兄最善,中兄次之,扁鹊最为下。
魏文侯曰:可得闻邪?
扁鹊曰:长兄于病视神,未有形而除之,故名不出于家。中兄治病,其在毫毛,故名不出于闾。若扁鹊者,镵血脉,投毒药,副肌肤,闲而名出闻于诸侯

鶡冠子·卷下·世贤第十六

图文无关,题图是瞎选的,我也不知道配什么图好.


为什么StringBuilder是线程不安全的?


原文始发于微信公众号(K字的研究):为什么StringBuilder是线程不安全的?

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/24799.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!