桑葚干,地塞米松,东阿阿胶-竞技宝电竞_竞技宝电竞官网_竞技宝电竞竞猜

admin 2周前 ( 11-01 22:23 ) 0条评论
摘要: 引言面试官:StringBuilder和StringBuffer的区别在哪?我:StringBuilder不是线程安全的,StringBuffer是线程安全的面试官:那String...

导言

面试官:StringBuilder和StringBuffer的差异在哪?

我:StringBuilder不是线程安全的,StringBuffer是线程安全的

面试官:那StringBuilder不安全的点在哪儿?

我:。。。(哑巴了)

在这之前我只记住了StringBuilder不是线程安全的,StringBuffer是线程安全的这个定论,至于StringBuilder为什么不安全从来没有去想过。

剖析

在剖析这个问题之前,咱们要知道StringBuilder和StringBuffer的内部完成跟String类相同,都是经过一个char数组存储字符串的

不同的是String类里边的char数组是final润饰的,是不可变的,而StringBuilder和StringBuffer的char数组是可变的。

首要经过一段代码去看一下多线程操作StringBuilder目标会呈现什么问题

p桑葚干,地塞米松,东阿阿胶-竞技宝电竞_竞技宝电竞官网_竞技宝电竞竞猜ublic class 桑葚干,地塞米松,东阿阿胶-竞技宝电竞_竞技宝电竞官网_竞技宝电竞竞猜St桑葚干,地塞米松,东阿阿胶-竞技宝电竞_竞技宝电竞官网_竞技宝电竞竞猜ringBuilderDemo {
public static void main(String[] args) throws InterruptedException {
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < 10; i++){
new Thread(new Runnable() {
@Override
public void run() {
for 王立群读史记全集目录(int j = 0; j < 1000; j++){
stringBuilder.append("a");
}
}
}).start();
}
Thread.sleep(100);
System.out.println(stringBuilder.length());
}
}

咱们能看桑葚干,地塞米松,东阿阿胶-竞技宝电竞_竞技宝电竞官网_竞技宝电竞竞猜到这段代码创建了10个线程,每个线程循环1000次往StringBuilder目标里边append字符。

正常情况下代码应该输出10000,可是实践运行会输出什么呢?



咱们看到输出了“9326”,小于预期的10000,而且还抛出了一个ArrayIndexOutOfBoundsException反常(反常不是必现曹海进)。

1、为什么输出值跟预期值不相同

咱们先看一下StringBuilder的两个成员变量(这两个成员变量实践上是界说在AbstractStringBuilder里边的,StringBuilder和StringBuffer都承继了Abstr乳妈actStringBuilder)

//存储字符串的具体内容
char[] value;
//现已运用的字符数组的数量
int count;

再看StringBuilder的append()办法:

@Override
public StringBuilder append(String str) {
su别舔了per.append(str);
return this;
}

StringBuilder的append()方密桃社法调用的父类AbstractStringBuilder的append()办法

public AbstractStringBuilder append(String str) {
if (str == nul锚草论l)
哈宝530return appendNull();
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len健美祖母, value, count);
count += len;
return thi全木海视频s;
}

咱们先不论代码的第五行和第六行干了什么,直接看第七行,count += len不是一个原子操作。

假定这时count值为10,len值为1,两个线程一起履行到了第七行,拿到的count值都是10,履行完加法运算后将成果赋值给count,所以两个线程履行完后count值为11,而不是12。

这便是为什么测验代码输出的值要比10000小的原因。

2、为什么会抛出Arra暖心论题瓶yIndexOutOfBoundsException反常。

咱们看回AbstractStringBuilder的append()办法源码的第五普法栏目剧双面人魔行,ensureCapacityInternal()办法是查看StringBuilder目标的原char数组的容量能不能盛下新的字符串,假如盛不下就调用expandCapacity()办法对char数组进行扩容。

private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
if (minimumCapacity - value.length > 0)
expandCapacity(minimumCapacity);
}

扩容的逻辑便是new一个新的char数组,新的char数组的容量是本来char数组的两倍再加2,再经过System.arryCopy()函数将原数组的内容复制到新数组,最终将指针指向新的char数组。

void expandCapacity(in韩庚姚星彤晒结婚证t minimumCapacity) {
//核算新的容量
int newCapacity = v桑葚干,地塞米松,东阿阿胶-竞技宝电竞_竞技宝电竞官网_竞技宝电竞竞猜alue.length * 2 + 2;
//中心省掉了一些查看逻辑
...
value = Arrays.copyOf(value, newCapacity);
}

Arrys.copyOf()办法

public static char[] copyOf(char[] original, int newLength) {
char[] copy = new char[newLength];
//复制数组
System.arboyfunraycopy(original, 0, copy, 0,
Math.min(original.length, newLength神仙池路口));
return copy;
}

AbstractStringBuilder的append()办法源码的第六行,是将String目标里边char数组里边的内容复制到StringBuilder目标的char数组里边,代码如下:

str.getChars(0, len, value, count);

get棋魂同人命运之力Chars()办法

public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
//中心省掉了一些查看
...
System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
}

复制流程见下图



假定现在有两个线程一起履行了StringBuilder的append()办法,两个线程都履行完了第五行的en霸爱小魔女sureCapacityInternal()办法,此时count=5。



这个时分线程1的cpu时刻片用完了,线程2持续履行。线程2履行完整个append()办法后count变成6了



线程1持续履行第六行的str.getChars()办法的时分拿到的count值便是6了,履行char数组复制的时分就会抛出ArrayIndexOutOfBoundsException反常。

至此,StringBuilder为什么不安全现已剖析完桑葚干,地塞米松,东阿阿胶-竞技宝电竞_竞技宝电竞官网_竞技宝电竞竞猜了。假如我易人珠们将测验代码的StringBuild香苗er目标换成StringBuf桑葚干,地塞米松,东阿阿胶-竞技宝电竞_竞技宝电竞官网_竞技宝电竞竞猜fer目标会输出什么呢?



当然是输出10000啦移楼公司!

那么StringBuffer用什么手法确保线程安全的?这个问题你点进StringBuffer的append()办法里边就知道了。

文章版权及转载声明:

作者:admin本文地址:http://lovetoeros.com/articles/1827.html发布于 2周前 ( 11-01 22:23 )
文章转载或复制请以超链接形式并注明出处竞技宝电竞_竞技宝电竞官网_竞技宝电竞竞猜