Java Questions 11 - 20  «Prev  Next»

Java SE 25 Threads, Synchronization, StringBuffer, and Monitor Methods

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.

  1. What is the difference between StringBuffer and StringBuilder?
    Answer:
    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());
  2. What is the purpose of Collections.synchronizedList()?
    Answer:
    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);
        }
    }
  3. Are all operations on a list returned by Collections.synchronizedList() automatically safe?
    Answer:
    No. Individual method calls are synchronized, but compound actions may still require external synchronization. A compound action is an operation made from multiple steps, such as checking whether a list contains an element and then adding that element. Iteration over a synchronized list should also be performed inside a synchronized block using the list itself as the lock.
    List<String> names =
            Collections.synchronizedList(new ArrayList<>());
    
    synchronized (names) {
        for (String name : names) {
            System.out.println(name);
        }
    }
  4. In Java SE 25, is placing synchronized before a method the only way to synchronize code?
    Answer:
    No. A method can be declared with the 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.

  5. What is thread deadlock?
    Answer:
    Deadlock occurs when two or more threads are blocked forever because each thread is waiting for a lock held by another thread. For example, one thread may hold the lock for a file object while waiting for a printer object, while a second thread holds the printer lock and waits for the file lock. Since neither thread releases its current lock, neither thread can continue.
    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.

  6. What are the purposes of wait(), notify(), and notifyAll()?
    Answer:
    These methods support coordination between threads that share an object's monitor. A thread calls 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.
  7. What must be considered when using wait(), notify(), and notifyAll()?
    Answer:
    The current thread must own the object's monitor before calling these methods. Therefore, they must be called from inside a synchronized method or a synchronized block that locks the same object. If a thread calls one of these methods without owning the monitor, Java throws 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.

  8. To which class do wait(), notify(), and notifyAll() belong?
    Answer:
    They belong to 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();
    }
  9. How does a thread enter an object's waiting set?
    Answer:
    A thread enters an object's waiting set by calling 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();
        }
    }
  10. What must be true for a thread to call wait(), notify(), or notifyAll()?
    Answer:
    The thread must own the monitor lock for the object on which the method is called. Owning the lock means the call occurs inside a synchronized method or synchronized block using that same object as the monitor.
    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.


SEMrush Software