StringBuilder не является потокобезопасным, но пока вы используете его из одного потока, вам не нужно беспокоиться об этом. Даже если у вас есть доступ из двух потоков, вы можете легко сделать его потокобезопасным, заключив критическую секцию в синхронизированный блок, например,
private StringBuilder sb = new StringBuilder();
void appendSomething(String v) {
synchronized (sb) {
sb.append("You entered ");
sb.append(v);
}
}
Если весь метод является критическим разделом, вы можете сказать, синхронизирован по методу
synchronized void appendSomething(String v) {
sb.append("You entered ");
sb.append(v);
}
Примечание. Я явно написал два оператора добавления, чтобы продемонстрировать, что простое использование потокобезопасного StringBuffer не сделает код потокобезопасным. Если вы запустили два потока со StringBuffer, то каждое добавление может быть синхронизировано, но все равно будет возможно возникновение условий гонки.
private StringBuffer sb = new StringBuffer();
void appendSomething(String v) {
sb.append("You entered ");
// Race condition!!!
sb.append(v);
}
Таким образом, два потока с надписью "hello" и "world" могут привести к выводу "Вы вошли Вы вошли в helloworld", потому что каждое добавление защищено не всем действием.
Использование синхронизированных классов также менее эффективно. Например, в приведенном выше примере оба вызова на добавление синхронизируются, поэтому в дополнение к условию гонки мне нужно дважды заблокировать объект по сравнению с одним разом с несинхронизированным классом.