StringBuilder的线程为什么不安全
StringBuffer和StringBuilder的区别在哪里?
StringBuffer是线程安全的,StringBuilder是线程不安全的。
那么StringBuilder不安全在哪里?在想这个问题前,我们要知道StringBuffer和StringBuilder的内部实现和String类是一样的,都是通过一个char数组存储字符串,不同的是String类的char数组是final修饰的,是不可变的;但是StringBuffer和StringBuilder的char数组是可变的。
例子:
多线程操作StringBuilder对象
public class StringBuilderDemo{
public static void main(String[] args) throws InterruptedException{
StringBuilder sBuilder = new StringBuilder();
for(int i=0;i<10;i++){
new Thread(new Runnable(){
@Override
public void run(){
for(int j=0;j<1000;j++){
sBuilder.append("a");
}
}
}).start();
}
Thread.sleep(100);
System.out.println(sBuilder.length());
}
}
运行结果
7346
这段代码创建了10个线程,每个线程循环1000次往StringBuilder对象里append字符,正常情况应输出10000;但是结果是小于预期结果,在实际运行中还可能抛出异常“ArrayIndexOutOfBoundsException”
那么,问题来了,为什么输出值和预期不一样?为什么会抛出异常“ArrayIndexOutOfBoundsException”?
来看第一个问题:
StringBuilder的两个成员变量(这两个变量定义在AbstractStringBuilder里面,StringBuilder和StringBuffer都继承了AbstractStringBuilder)
//存储字符串的具体内容
char[] value;
//已经使用的字符数组的数量
int count;
在看StringBuilder的append()方法
@Override
public StringBuilder append(String str){
super.append(str);
return this;
}
StringBuilder的append()方法调用AbstractStringBuilder的append()方法
public AbstractStringBuilder append(String str){
if(str==null){
return appendNull();
}
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0,len,value,count);
count += len;
return this;
}
看第8行,count += len; 假设count为10,len值为1,两个线程同时执行到这里,拿到的count值都为10,执行完加法运算后将值赋值给count,结果两个线程执行完后的count值为11,而不是12,这就是输出值比预期值要小的原因。
看第二个问题
回看AbstractStringBuilder的append()方法第6行
ensureCapacityInternal()方法是检查StringBuilder()对象的原char数组的容量能不能装下新的字符串,如果装不下就调用expandCapacity()方法对char数组进行扩容
private void ensureCapacityInternal(int minimumCapacity){
if(minimumCapacity - value.length>0)
expandCapacity(minimumCapacity);
}
扩容即是new一个新的char数组,在将原来的数组内容复制到新的数组,最后将指针指向新的char数组
void expandCapacity(int minimunCapacity){
//计算新的容量
int newCapacity = value.length*2+2;
//省略一些检查逻辑
...
value = Array.copyOf(value,newCapacity);
}
Array.copyOf()方法
public static char[] copyOf(char[] original,int newLength){
char[] copy = new char[newLength];
//拷贝数组
System.arraycopy(original,0,copy,0,Mathmin(original.length,newLength));
return copy;
}
AbstractStringBuilder的append()方法第7行,是将String对象的char数组内容拷贝到StringBuilder对象的char数组里面
str.getChars(0,len,value,count);
getChars()方法
public void getChars(int srcBegin,int srcEnd,char dst[],int dstBegin){
//
...
System.arraycopy(value,srcBegin,dst,dstBegin,srcEnd-srcBegin);
}
拷贝流程:

假设现在有两个线程同时执行StringBuilder的append()方法,两个线程都执行完了ensureCapacityInternal()方法,此时count=5

此时线程1的cpu时间片用完了,线程2继续执行,线程2执行完append()方法后变为6

线程1继续执行str,getChars()方法的时候拿到的值是6,执行char数组拷贝的时候就会抛出异常ArrayIndexOutOfBoundsException。
解释完毕!
那么将StringBuilder换成StringBuffer会发生什么呢?
StringBuffer可是个线程安全的StringBuffer,当然是输出10000啦。
StringBuilder的线程为什么不安全的更多相关文章
- StringBuilder是不是线程安全的?
测试条件: 开启2个并行执行任务,往同一个StringBuilder对象写入值 测试代码: ; static StringBuilder sbIsThreadSafe = new StringBuil ...
- 为什么StringBuilder是线程不安全的?StringBuffer是线程安全的?
面试中经常问到的一个问题:StringBuilder和StringBuffer的区别是什么? 我们非常自信的说出:StringBuilder是线程安全的,StirngBuffer是线程不安全的 面试官 ...
- StringBuffer(线程安全)StringBuilder(非线程安全)
StringBuffer属于线程安全,相对为重量级 StringBuilder属于非线程安全,相对为轻量级 线程安全的概念: 网络编程中许多线程可能会同时运行一段代码.当每次运行结果和单独线程运行的结 ...
- StringBuilder为什么线程不安全(面试必问)
文章转载自:https://juejin.im/post/5d6228046fb9a06add4e37fe 作者:千山qianshan 1.引言 周五去面试又被面试的一个问题问哑巴了 面试官:Stri ...
- 数据结构(逻辑结构,物理结构,特点) C#多线程编程的同步也线程安全 C#多线程编程笔记 String 与 StringBuilder (StringBuffer) 数据结构与算法-初体验(极客专栏)
数据结构(逻辑结构,物理结构,特点) 一.数据的逻辑结构:指反映数据元素之间的逻辑关系的数据结构,其中的逻辑关系是指数据元素之间的前后件关系,而与他们在计算机中的存储位置无关.逻辑结构包括: 集合 数 ...
- 手把手实例对比String、StringBuilder字符串的连接效率及StringBuilder和StringBuffer线程安全的比较
一.字符串连接的效率问题 使用String连接字符串时为什么慢? 小知识点 java中对数组进行初始化后,该数组所占的内存空间.数组长度都是不可变的. 创建一个字符串,为字符串对象分配内存空间,会耗费 ...
- StringBuffer与StringBuilder的简单理解
联系:两者都适用于字符串的操作,都可以随便对字符串的内容进行变更操作,都继承至AbstractStringBuilder. 区别:StringBuffer是线程安全的,方法都加了synchronize ...
- JAVA中的线程安全与非线程安全
原文:http://blog.csdn.net/xiao__gui/article/details/8934832 ArrayList和Vector有什么区别?HashMap和HashTable有什么 ...
- String、StringBuffer和StringBuilder的深入解析
今天闲来无事,整理了下平时记录在印象笔记里的java开发知识点,整理到String,StringBuffer以及StringBuilder的区别时突然又产生了新的疑惑,这些区别是怎么产生的?温故为何能 ...
随机推荐
- 设计模式之GOF23解释器模式
解释器模式Interpreter -是一种不常用的设计模式 -用于描述如何构成一个简单的语言解释器,主要用于使用面向对象语言开发的编译器和解释器设计 -当我们需要开发一种新的语言时,可以考虑使用解释器 ...
- 走向统一的 .NET 旅程
这是微软第一次完全线上举办的Build大会,也是第一次完全属于开发者的大会.几乎所有的新产品都是属于开发者,开发者成为了唯一的主角. 现在的微软比以往任何时候都贴近开发者,重视开发者的作用,为他们打造 ...
- influxes 基本概念
Influxes 基本概念 1.安装 两种,虚机的话直接下载二进制文件起就好了,容器也很方便,存储挂载到/var/lib/influxdb 起就ok,配置文件可以通过configmap挂载进去. 2. ...
- centos6下filebeat多开问题
centos6下filebeat多开问题 0. 场景 比如之前在用filebeat做收集,但是想新开一个实例把之前的日志全部重新导一遍,如果直接指定filebeat -c 是不行的,因为filebea ...
- 「雕爷学编程」Arduino动手做(32)——雨滴传感器模块
37款传感器与模块的提法,在网络上广泛流传,其实Arduino能够兼容的传感器模块肯定是不止37种的.鉴于本人手头积累了一些传感器和模块,依照实践出真知(一定要动手做)的理念,以学习和交流为目的,这里 ...
- HTML标签和属性一
一.web基础知识 html,专门指网页技术 HTML5,大前端技术(网页,app,桌面程序,数据可视化,VR....) 网页(pc,pad,phone) app wx 服务器 数据库 HTML5 ...
- Angular 服务对象的作用范围
1.高阶话题:服务对象的作用范围 声明服务提供者的方式: 方式1:在根模块中提供服务对象-----在整个应用中服务是单例 @Injectable({ providedIn:'root' }) expo ...
- 如何分析和提高(C/C++)程序的编译速度?
版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://www.cnblogs.com/lihuidashen/p/129354 ...
- CF820D Mister B and PR Shifts
题目链接:http://codeforces.com/problemset/problem/820/D 题目大意: 给出一个\(n\)元素数组\(p[]\),定义数组\(p[]\)的误差值为\(\su ...
- python-基站位置查询
本文采用的接口是聚合数据提供的 python2.7环境,3.x环境试了下好像不支持,获取位置信息为空 如下为运用代码: #coding=utf-8 ''' Created on 2019年9月18日 ...