В одиночных потоках StringBuffer не намного медленнее, чем StringBuilder , благодаря оптимизации JVM. А в многопоточности вы не можете безопасно использовать StringBuilder.
Вот мой тест (не тест, просто тест):
public static void main(String[] args) {
String withString ="";
long t0 = System.currentTimeMillis();
for (int i = 0 ; i < 100000; i++){
withString+="some string";
}
System.out.println("strings:" + (System.currentTimeMillis() - t0));
t0 = System.currentTimeMillis();
StringBuffer buf = new StringBuffer();
for (int i = 0 ; i < 100000; i++){
buf.append("some string");
}
System.out.println("Buffers : "+(System.currentTimeMillis() - t0));
t0 = System.currentTimeMillis();
StringBuilder building = new StringBuilder();
for (int i = 0 ; i < 100000; i++){
building.append("some string");
}
System.out.println("Builder : "+(System.currentTimeMillis() - t0));
}
Результаты:
строки: 319740
Буферы: 23
Строитель: 7!
Таким образом, Builders быстрее, чем Buffers, и WAY быстрее, чем конкатенация строк.
Теперь давайте используем Executor для нескольких потоков:
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);
}
}
Теперь StringBuffers принимают 157 мс для 100000 добавлений. Это не тот же тест, но по сравнению с предыдущими 37 мс, вы можете смело предположить, что добавления StringBuffers медленнее при использовании многопоточности . Причина в том, что JIT / горячая точка / компилятор / что-то оптимизирует, когда обнаруживает, что для проверки блокировок нет .
Но с StringBuilder, у вас есть java.lang.ArrayIndexOutOfBoundsException , потому что параллельный поток пытается добавить что-то, где он не должен.
Вывод: вам не нужно гоняться за StringBuffers. А если у вас есть темы, подумайте о том, что они делают, прежде чем пытаться набрать несколько наносекунд.