先看一下StringBuilder和StringBuffer的构成
public final class StringBuffer extends AbstractStringBuilder implements java.io.Serializable, CharSequence {......} public final class StringBuilder extends AbstractStringBuilder implements java.io.Serializable, CharSequence {....}
两个类继承了同一个类,也实现了同样的接口,那么导致线程不安全的方法肯定在类内部自身的方法了。
再来看一下他们共同的父类
abstract class AbstractStringBuilder implements Appendable, CharSequence { char[] value; int count;
}
该类有两个重要的成员变量 一个是count,一个是value,count是字符个数,也就是长度。value则为具体的字符数组。
再来看一下常见的append(String str)方法
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; }
讲解一下这个代码,首先数组的长度在创建的时候就是固定的,想一个原先大小为5的数组 想再添加n个元素 做法是创建一个新得5+n长度的数组,先优先放原先的数组,再在数组的长度(count)处开始添加需要添加进来的元素。
String的本质就是char[] ,所以这个方法里的str.getChars()方法不难猜出是将原先字符数组和新传进来的字符数组大小相加,创建一个不小于该长度的新数组,先优先放入旧数组,再根据旧数组的长度作为索引存入新数组,在这个过程里,count有着举足轻重的作用,他标记了需要在新字符数组的哪个索引处开始添加。如果count混乱,最后的值肯定也是不准确的。所以关键点就是方法是否加锁。
StringBuffer: public synchronized StringBuffer append(String str) { toStringCache = null; super.append(str); return this; } StringBuilder: public StringBuilder append(String str) { super.append(str); return this; }
最后来看一下String.getChars()方法来验证一下以上是否正确
public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) { if (srcBegin < 0) { throw new StringIndexOutOfBoundsException(srcBegin); } if (srcEnd > value.length) { throw new StringIndexOutOfBoundsException(srcEnd); } if (srcBegin > srcEnd) { throw new StringIndexOutOfBoundsException(srcEnd - srcBegin); } System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin); }
原文:https://www.cnblogs.com/Vinlen/p/13597079.html