StringBuffer
синхронизируется, но StringBuilder
нет! Это было подробно обсуждено в Различия между StringBuilder и StringBuffer.
Здесь есть пример кода (Answer by @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
at getChars
метод, который вызывает 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
?!