【发布时间】:2015-07-13 05:07:22
【问题描述】:
StringBuffer 是同步的,但 StringBuilder 不是!这已在Difference between StringBuilder and StringBuffer 进行了深入讨论。
那里有一个示例代码(由@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.classgetChars 调用System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin); 的方法根据System.arraycopy javadoc:
从指定的源数组复制一个数组,从 指定位置,到目的地的指定位置 大批。从源复制数组组件的子序列 src 引用的数组到 dest 引用的目标数组。 复制的组件数量等于长度参数。 ....
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 当然!!我已经询问了详细信息,为什么会出错?!
标签: java multithreading synchronization thread-safety stringbuilder