The reason StringBuffer is thread-safe is that back in the day when the first version of the java api was designed, people approached concurrency differently than nowadays. The prevailing opinion was that objects should be thread safe - because Java supports threads, and people might use any JDK class in multiple threads. Later, when Java was starting to be optimized for execution time, the cost of those needless synchronization blocks started to become a problem, so newer APIs were designed to not be synchronized. Still later, the JVM started to optimize locks to the point that uncontested locks became essentially free, making the entire decision a moot point.
StringBuffer is still thread-safe, because old code might rely on it being thread-safe. That is far from typical use, but conceivable.
For instance, suppose you were writing a logfile appender that forwards log entries to a central server. Since we don't want to block the caller while waiting for network I/O we do that in a dedicated thread. The other threads would accumulate their log entries in a StringBuffer:
class RemoteLogger implements Runnable, Appender {
final StringBuffer buffer = new StringBuffer();
void append(String s) {
buffer.append(s);
}
public void run() {
for (;;) {
Thread.sleep(100);
String message = buffer.toString();
sendToServer(message);
buffer.delete(0, message.length());
}
}
}