Back to String & String Pool
Q6Question 6 of 12

How is StringBuffer thread-safe compared to String and StringBuilder?

Key Concepts
  • StringBuffer methods (append, insert, delete) are synchronized – only one thread at a time
  • StringBuilder has the same API but NO synchronization – faster for single-threaded use
  • String is thread-safe by being immutable – no shared mutable state
  • In modern Java, StringBuilder + proper design (thread-local building) is preferred over StringBuffer
  • Understanding this concept is essential for Java interviews at senior level

Definition

StringBuffer's methods are synchronized. Only one thread can modify it at a time. StringBuilder is identical but unsynchronized and faster. Use StringBuilder by default.

Thread safety comparison:

  • String: Thread-safe by immutability (no state changes).
  • StringBuffer: Thread-safe by synchronization (every method is synchronized).
  • StringBuilder: NOT thread-safe. Fastest for single-threaded code.

String is a printed receipt (read-only, any number of people can read it). StringBuffer is a shared whiteboard with a lock (one person writes at a time). StringBuilder is a personal whiteboard (fast, no locking, one person only).

Understanding the Concepts

StringBuffer synchronization

JAVA
// StringBuffer: every append is synchronized
StringBuffer sb = new StringBuffer();
// Thread 1: sb.append("TX001");  // acquires lock, appends, releases
// Thread 2: sb.append("TX002");  // waits for lock, then appends
// Result: "TX001TX002" (order guaranteed, no data corruption)

// StringBuilder: NO synchronization
StringBuilder sb2 = new StringBuilder();
// Thread 1: sb2.append("TX001");  // no lock
// Thread 2: sb2.append("TX002");  // no lock
// Result: may corrupt internal array → ArrayIndexOutOfBoundsException

When do you actually need StringBuffer?

Almost never in modern Java. Instead:

JAVA
// Each thread builds its own StringBuilder (no sharing)
List<String> results = accounts.parallelStream()
    .map(account -> {
        StringBuilder sb = new StringBuilder();  // thread-local, no sharing
        sb.append(account.getAccountId()).append(": ").append(account.getBalance());
        return sb.toString();
    })
    .collect(Collectors.toList());

What should a senior engineer know?

  • StringBuffer is legacy (Java 1.0). StringBuilder was added in Java 1.5 as the unsynchronized replacement.
  • Synchronization overhead is ~10-20% even without contention due to lock acquisition.
  • If you truly need thread-safe string building, use thread-local StringBuilders or synchronize at a higher level.

What are common mistakes?

A frequent mistake is using == to compare String content instead of equals().

Wrong approach:

JAVA
package com.bank.service;

public class AccountValidator {
    public boolean validateAccountType(String type) {
        // WRONG: == compares references, not content
        if (type == "SAVINGS") {
            return true;
        }
        return false;
    }
}

Correct approach:

JAVA
package com.bank.service;

public class AccountValidator {
    public boolean validateAccountType(String type) {
        // CORRECT: equals() compares content
        if ("SAVINGS".equals(type)) {  // null-safe: literal first
            return true;
        }
        return false;
    }
}

Using the literal on the left side of equals() prevents NullPointerException when the variable is null. This is a standard practice at National Bank of India for all string comparisons in service code.

What are the edge cases?

String behavior has several surprising edge cases that can catch banking developers off guard.

JAVA
package com.bank.edge;

public class StringEdgeCases {
    public static void main(String[] args) {
        // Edge case 1: compile-time constant folding
        String a = "SAV" + "2001";       // Compile-time constant
        String b = "SAV2001";
        System.out.println(a == b);       // true (same pool entry)

        // Edge case 2: variable concatenation is NOT pooled
        String prefix = "SAV";
        String c = prefix + "2001";       // Runtime concatenation
        System.out.println(b == c);       // false (different objects)

        // Edge case 3: final variable IS a compile-time constant
        final String finalPrefix = "SAV";
        String d = finalPrefix + "2001";  // Compile-time constant
        System.out.println(b == d);       // true (same pool entry)

        // Edge case 4: substring no longer shares char array (Java 7u6+)
        String full = "AccountNumber:SAV2001";
        String sub = full.substring(14); // New String, new char array
    }
}
TEXT
true
false
true

The final keyword makes the variable a compile-time constant, which changes how the compiler handles concatenation.

What are common interview follow-up questions?

Interviewers often follow up with these related questions:

  • How does this concept apply to a high-throughput banking transaction system?
  • What are the performance implications in a production environment?
  • How would you debug issues related to this concept?
  • What tools would you use to monitor this in production?
  • How does this behave differently in Java 8 vs Java 17+?

When answering follow-up questions, always relate your answer back to practical experience. For example, at National Bank of India, Rajesh debugged a production issue related to this concept by using JFR recordings and heap dumps. Providing concrete examples from banking systems shows the interviewer that you understand both the theory and the practice.

Visual Overview

DIAGRAM
  String:        immutable → thread-safe (no changes possible)
  StringBuffer:  synchronized methods → thread-safe (one writer at a time)
  StringBuilder: no synchronization → NOT thread-safe (fastest)

  Modern: each thread gets its own StringBuilder → no sharing → no need for StringBuffer

Key Takeaway

StringBuffer is thread-safe because its methods are synchronized. Only one thread can modify it at a time. StringBuilder has the same API but no synchronization, making it faster. In modern Java, use StringBuilder by default and avoid sharing it across threads.

One sentence to remember: StringBuffer is synchronized (thread-safe), StringBuilder is not (faster) – use StringBuilder and avoid sharing.

---

Share
Use ← → arrow keys to navigate