Why StringBuilder Fails in Multithreaded Code and How StringBuffer Solves It
This article explains why StringBuilder is not thread‑safe, demonstrates the problem with a multithreaded example that produces a wrong length and occasional ArrayIndexOutOfBoundsException, and shows how StringBuffer’s synchronized implementation avoids these issues.
Introduction
During a recent interview I was asked about the difference between StringBuilder and StringBuffer and got stuck when the interviewer probed the thread‑safety aspect.
Interviewer: What is the difference between StringBuilder and StringBuffer? Me: StringBuilder is not thread‑safe, StringBuffer is thread‑safe. Interviewer: Where exactly is StringBuilder unsafe? Me: ... (stumped)
I only remembered the high‑level conclusion and never examined why StringBuilder is unsafe.
Analysis
StringBuilder, StringBuffer, and String share the same internal representation: a mutable char[] array. Unlike the immutable String whose array is final , the arrays in StringBuilder and StringBuffer can change.
To see the problem we run the following multithreaded code:
public class StringBuilderDemo {
public static void main(String[] args) throws InterruptedException {
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < 10; i++) {
new Thread(new Runnable() {
@Override
public void run() {
for (int j = 0; j < 1000; j++) {
stringBuilder.append("a");
}
}
}).start();
}
Thread.sleep(100);
System.out.println(stringBuilder.length());
}
}We expect the length to be 10 000, but the program often prints a smaller number (e.g., 9326) and may throw an ArrayIndexOutOfBoundsException.
1. Why the output differs from the expected value
StringBuilder (inherited from AbstractStringBuilder) contains two fields:
char[] value; // the character array
int count; // number of used charactersThe append() method ultimately executes:
public AbstractStringBuilder append(String str) {
if (str == null) return appendNull();
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}The statement count += len is not atomic. If two threads read the same count value simultaneously, both will write back the same incremented result, causing lost updates and a final length smaller than 10 000.
2. Why an ArrayIndexOutOfBoundsException may be thrown
The method ensureCapacityInternal() checks whether the current value array can accommodate the new characters and expands it if necessary:
private void ensureCapacityInternal(int minimumCapacity) {
// overflow‑conscious code
if (minimumCapacity - value.length > 0)
expandCapacity(minimumCapacity);
}Expansion creates a new array with roughly double the previous size plus two, then copies the old contents:
void expandCapacity(int minimumCapacity) {
int newCapacity = value.length * 2 + 2;
// ... some checks omitted
value = Arrays.copyOf(value, newCapacity);
}The copy operation uses System.arraycopy internally:
public static char[] copyOf(char[] original, int newLength) {
char[] copy = new char[newLength];
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}When two threads interleave, one thread may finish the capacity check and increment count to 6, while the other thread still uses the old count value (5) in the subsequent str.getChars() call. The copy then writes beyond the current array bounds, triggering ArrayIndexOutOfBoundsException.
Thus the root cause of StringBuilder’s unsafety is the lack of atomicity in updating count and the non‑synchronized expansion of the internal buffer.
If we replace StringBuilder with StringBuffer in the same test, the program reliably prints 10 000 because StringBuffer synchronizes its mutating methods, guaranteeing thread safety.
Inspecting StringBuffer ’s append() implementation reveals the use of the synchronized keyword, which serializes access and prevents the race conditions described above.
END
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Programmer DD
A tinkering programmer and author of "Spring Cloud Microservices in Action"
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
