"Thread safety" is a slippery idea.
When somebody says that a class such as StringBuffer is "thread safe" they usually mean that an instance of the class will not behave in weird, unexpected ways when it is accessed by more than one thread. For example, if thread A tries to append "yellow" to a StringBuffer at the same time as thread B appends "green", the final value might be "yellowgreen" or it might be "greenyellow", but it won't be "yegrelleown". No IllegalStateException will be thrown, and it won't crash the JVM.
Those guarantees make each individual instance of the class thread safe, but they won't make your program thread safe. If your program has some thread C that tries to read the StringBuffer at the same time, then it might get either of the two values above, or it might get just "yellow", or just "green", or even the empty string. If it's important for thread C to see both color names in the string, then you will need additional synchronization to guarantee that it happens that way.
Building a program entirely out of "thread-safe" classes does not automatically make the whole program "thread safe."
As other answers have already told you, the StringBuffer class achieves thread-safety by making all of its methods synchronized
, but that is not the only way to achieve thread-safety, and in some cases, that may not be enough to achieve it. If you want to know what is thread-safe and what isn't, it's important to read the documentation.
Also, when you read it, pay attention to how the class was meant to be used. If somebody advertizes a class as "thread safe" in some particular use-case, that doesn't mean it will be "thread safe" in other use cases