StringBuffer
是同步的,但StringBuilder
不是!這已在Difference between StringBuilder and StringBuffer深入討論。究竟是什麼原因導致StringBuilder在多線程環境中失敗
有一個例子代碼存在(由@NicolasZozol回答),其解決兩個問題:
- 比較的這些
StringBuffer
和StringBuilder
- 性能顯示
StringBuilder
可以在多線程環境中失敗。
我的問題是關於第二部分,究竟是什麼讓它出錯?! 當您運行代碼有時,堆棧跟蹤顯示如下:
Exception in thread "pool-2-thread-2" java.lang.ArrayIndexOutOfBoundsException
at java.lang.String.getChars(String.java:826)
at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:416)
at java.lang.StringBuilder.append(StringBuilder.java:132)
at java.lang.StringBuilder.append(StringBuilder.java:179)
at java.lang.StringBuilder.append(StringBuilder.java:72)
at test.SampleTest.AppendableRunnable.run(SampleTest.java:59)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:722)
當我追查下來,我發現,這實際上拋出異常的類代碼:String.class
在getChars
方法,它調用System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
根據到System.arraycopy
的Javadoc:
複製從指定的源陣列的陣列,在所述 指定位置開始,到目的地 陣列的指定位置。將數組組件的子序列從src引用的源數組012h複製到dest引用的目標數組。 複製的組件數等於length參數。 ....
IndexOutOfBoundsException - 如果複製將導致數據訪問 超出數組範圍。
爲了簡單起見我有完全的代碼粘貼到這裏:
public class StringsPerf {
public static void main(String[] args) {
ThreadPoolExecutor executorService = (ThreadPoolExecutor) Executors.newFixedThreadPool(10);
//With Buffer
StringBuffer buffer = new StringBuffer();
for (int i = 0 ; i < 10; i++){
executorService.execute(new AppendableRunnable(buffer));
}
shutdownAndAwaitTermination(executorService);
System.out.println(" Thread Buffer : "+ AppendableRunnable.time);
//With Builder
AppendableRunnable.time = 0;
executorService = (ThreadPoolExecutor) Executors.newFixedThreadPool(10);
StringBuilder builder = new StringBuilder();
for (int i = 0 ; i < 10; i++){
executorService.execute(new AppendableRunnable(builder));
}
shutdownAndAwaitTermination(executorService);
System.out.println(" Thread Builder: "+ AppendableRunnable.time);
}
static void shutdownAndAwaitTermination(ExecutorService pool) {
pool.shutdown(); // code reduced from Official Javadoc for Executors
try {
if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {
pool.shutdownNow();
if (!pool.awaitTermination(60, TimeUnit.SECONDS))
System.err.println("Pool did not terminate");
}
} catch (Exception e) {}
}
}
class AppendableRunnable<T extends Appendable> implements Runnable {
static long time = 0;
T appendable;
public AppendableRunnable(T appendable){
this.appendable = appendable;
}
@Override
public void run(){
long t0 = System.currentTimeMillis();
for (int j = 0 ; j < 10000 ; j++){
try {
appendable.append("some string");
} catch (IOException e) {}
}
time+=(System.currentTimeMillis() - t0);
}
}
能否請您更詳細描述(或用樣品)展示瞭如何多線程導致System.arraycopy
失敗,?!或者線程如何讓invalid data
傳遞給System.arraycopy
?!
StringBuilder的是相同的StringBuffer,那就是它存儲的對象堆,它也可以被修改。 StringBuffer和StringBuilder的主要區別在於StringBuilder不是線程安全的。 StringBuilder速度很快,因爲它不是線程安全的。 – therealprashant
@therealprashant當然!我已經要求瞭解詳情,爲什麼會出錯?! –