Java synchronization is based on object monitors, mutual exclusion, and controlled
communication between threads. In Java SE 25, the classic rules for
synchronized, wait(), notify(), and
notifyAll() still apply, but modern Java developers should also
understand when to use higher-level concurrency tools from
java.util.concurrent. This interview review focuses on core
synchronization concepts that remain relevant for Java SE 25.
StringBuffer and StringBuilder?
StringBuffer is synchronized, while StringBuilder is
not synchronized. Both classes represent mutable sequences of characters, but
StringBuffer synchronizes its methods where necessary so that a
single instance can be used more safely by multiple threads. In single-threaded
code, StringBuilder is usually preferred because it avoids
synchronization overhead.
StringBuilder builder = new StringBuilder();
builder.append("Java ");
builder.append("SE 25");
System.out.println(builder.toString());
StringBuffer buffer = new StringBuffer();
buffer.append("Thread-safe ");
buffer.append("mutable text");
System.out.println(buffer.toString());
Collections.synchronizedList()?
Collections.synchronizedList() returns a synchronized wrapper
around an existing List. Individual method calls on the returned
list are synchronized. This can protect basic list operations when multiple
threads share the same list instance.
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class SynchronizedListDemo {
public static void main(String[] args) {
List<String> names =
Collections.synchronizedList(new ArrayList<>());
names.add("Ada");
names.add("James");
System.out.println(names);
}
}
Collections.synchronizedList() automatically safe?
List<String> names =
Collections.synchronizedList(new ArrayList<>());
synchronized (names) {
for (String name : names) {
System.out.println(name);
}
}
synchronized before a method the only way to synchronize code?
synchronized modifier, but
synchronization can also be applied to a specific block of code. A synchronized
instance method uses the current object as its monitor. A synchronized static
method uses the Class object associated with the class.
public class Counter {
private int count;
public synchronized void incrementMethod() {
count++;
}
public void incrementBlock() {
synchronized (this) {
count++;
}
}
public synchronized int value() {
return count;
}
}
Java also provides higher-level concurrency utilities such as
ReentrantLock, Semaphore,
CountDownLatch, ConcurrentHashMap, and
BlockingQueue. These classes are often preferred for more complex
coordination because they provide clearer control than low-level monitor code.
public class DeadlockExample {
private final Object fileLock = new Object();
private final Object printerLock = new Object();
public void taskOne() {
synchronized (fileLock) {
synchronized (printerLock) {
System.out.println("Task one completed");
}
}
}
public void taskTwo() {
synchronized (printerLock) {
synchronized (fileLock) {
System.out.println("Task two completed");
}
}
}
}
A common prevention strategy is to acquire locks in a consistent global order.
wait(), notify(), and notifyAll()?
wait() when it cannot continue until a
condition changes. Another thread calls notify() or
notifyAll() after changing shared state so that waiting threads
may wake up and check the condition again.
notify() wakes one waiting thread. notifyAll() wakes
all waiting threads for that object's monitor. In many application designs,
notifyAll() is safer because multiple waiting threads may be
waiting for different conditions.
wait(), notify(), and notifyAll()?
IllegalMonitorStateException.
public class SharedFlag {
private boolean ready;
public synchronized void waitUntilReady() throws InterruptedException {
while (!ready) {
wait();
}
}
public synchronized void setReady() {
ready = true;
notifyAll();
}
}
The waiting condition should be tested in a loop, not a simple
if statement. A waiting thread can wake up and still find that the
condition it needs is not satisfied.
wait(), notify(), and notifyAll() belong?
java.lang.Object. This design makes sense because
every Java object has an associated monitor, and these methods operate on the
monitor of the object used for synchronization.
Object lock = new Object();
synchronized (lock) {
lock.notifyAll();
}
wait() on that
object while it owns the object's monitor. The call releases the monitor and
suspends the thread until it is notified, interrupted, or the specified wait
time expires.
synchronized (lock) {
while (!conditionIsReady()) {
lock.wait();
}
}
wait(), notify(), or notifyAll()?
private final Object monitor = new Object();
public void signal() {
synchronized (monitor) {
monitor.notifyAll();
}
}
These synchronization rules remain important in Java SE 25 because they explain how the Java monitor model works at the language level. Modern applications often combine these fundamentals with higher-level concurrency utilities, but a Java developer should still understand object locks, synchronized methods, synchronized blocks, deadlock, and monitor-based communication.