一、概述
在java開發(fā)時,我們經常會用到StringBuffer和StringBuilder,且都知道一個結論:StringBuilder不是線程安全的,StringBuffer是線程安全的,至于為什么?可能大多數人一知半解。
下面,我通過代碼舉例、StringBuffer和StringBuilder源碼分析進行解釋。
二、代碼舉例
1、驗證StringBuffer是線程安全的
/**
* 驗證StringBuffer線程安全,如下,如果length==1000,則可證明
* @throws InterruptedException
*/
public static void testStringBuffer() throws InterruptedException {
StringBuffer sb = new StringBuffer();
for (int i=0; i<10; i++){
new Thread(new Runnable() {
@Override
public void run() {
for (int j=0; j<1000; j++){
sb.append("a");
}
}
}).start();
}
Thread.sleep(100);
System.out.println(sb.length());
}
/**
* 主測試方法
* @param args
*/
public static void main(String[] args) {
try {
ThreadTest.testStringBuffer();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
上面,我們起10個線程,每個線程循環(huán)1000次往StringBuffer對象里面append字符。理論上應該輸出實例sb的字符串長度=10000,我們執(zhí)行代碼,實際輸出也是10000,業(yè)務上證明了StringBuffer是線程安全的。

2、驗證StringBuilder是線程不安全的
/**
* 驗證StringBuild線程不安全,如下,如果length!=1000,則可證明
* @throws InterruptedException
*/
public static void testStringBuild() throws InterruptedException {
StringBuilder sb = new StringBuilder();
for (int i=0; i<10; i++){
new Thread(new Runnable() {
@Override
public void run() {
for (int j=0; j<1000; j++){
sb.append("a");
}
}
}).start();
}
Thread.sleep(100);
System.out.println(sb.length());
}
/**
* 主測試方法
* @param args
*/
public static void main(String[] args) {
try {
ThreadTest.testStringBuild();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
上面,我們也起10個線程,每個線程循環(huán)1000次往StringBuilder對象里面append字符。我們也希望像StringBuffer一樣,得到10000的結果,但是我們運行代碼后,結果卻<10000。業(yè)務上證明了StringBuilder是線程不安全的。

三、源碼分析
我們通過查看StringBuffer和StringBuilder的append()方法,發(fā)現他們都調用父類AbstractStringBuilder的append()方法,分別如下:
StringBuffer重寫的append方法:
@Override
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
StringBuilder重寫的append方法:
@Override
public StringBuilder append(String str) {
super.append(str);
return this;
}
父類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;
}
通過上面的源碼,可知,StringBuffer和StringBuilder的append()的區(qū)別就是StringBuffer多了個 toStringCache = null; 這里,我們不再分析AbstractStringBuilder的append()方法的實現方式,大家可以自行了解,下面主要分析影響線程安全性的這段代碼的作用。
我們查看StringBuffer源碼,發(fā)現多了比StringBuilder多了一個參數:
public final class StringBuffer
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
/**
* A cache of the last value returned by toString. Cleared
* whenever the StringBuffer is modified.
*/
private transient char[] toStringCache;
//其他代碼,略
}
再看下StringBuffer的toString方法:
@Override
public synchronized String toString() {
if (toStringCache == null) {
toStringCache = Arrays.copyOfRange(value, 0, count);
}
return new String(toStringCache, true);
}
這里的作用就是如果StringBuffer對象此時存在toStringCache,在多次調用其toString方法時,其new出來的String對象是會共享同一個char[] 內存的,達到共享的目的。但是StringBuffer只要做了修改,其toStringCache屬性值都會置null處理。這也是StringBuffer和StringBuilder的一個區(qū)別點,也是StringBuffer為什么線程安全的原因。
四、結論
通過上面的舉例和源碼分析,我們可以知道為什么StringBuffer是線程安全的,StringBuilder是線程不安全的,至于他們的append()方法,都是集成父類的append()方法,大家可以網上去了解下具體實現原理。