Почему StringBuilder не thread-safe даже при одном writer и одном reader

Этим вопросом проверяют, понимает ли кандидат разницу между атомарностью операции и видимостью изменений между потоками. Ответ «там нет synchronized, поэтому не потокобезопасен» — правильный, но неполный: собеседник ждёт объяснения механизма, а не факта.

StringBuilder внутри держит char[] и счётчик длины. Даже если один поток пишет через append(), а другой только читает через toString(), между потоками нет happens-before связи. Без неё JIT и процессор имеют право не публиковать промежуточное состояние массива и счётчика вовремя — читающий поток может увидеть длину, не соответствующую фактическому содержимому буфера.

StringBuffer синхронизирует каждый метод, поэтому safe, но платит за это локом на каждый вызов даже при отсутствии конкуренции. На практике для многопоточного сценария правильный ответ — не StringBuffer, а внешняя синхронизация точки публикации результата, либо построение строки локально в потоке и передача уже готового immutable String через безопасный канал (volatile-поле, очередь, финальная переменная).

StringBuilder sb = new StringBuilder(); sb.append("partial"); // сюда читает другой поток // без happens-before - UB

Следом спросят: а что, если StringBuilder создаётся и полностью заполняется в конструкторе, а потом объект публикуется через final поле другого класса? Ответ: final поле само по себе не даёт happens-before для содержимого StringBuilder — гарантия final работает только для случая корректно опубликованного неизменяемого объекта, а StringBuilder изменяемый. Единственный надёжный вариант — сконвертировать в String через toString() до публикации и передавать дальше готовую неизменяемую строку.

StringBuilder не про потокобезопасность вообще — он про быструю конкатенацию в одном потоке. Как только объект должен пересечь границу потоков, конвертируйте в String до этой границы.

Тренажёр: 600 вопросов, мок с таймером, план повторов

senior·base — что спрашивают на самом деле


В этом посте были ссылки, но мы их удалили по правилам Сетки