Java Questions 11 - 20  «Prev  Next»

Synchronized Collections and Monitors in Java SE 25

These ten questions distinguish synchronized operations from complete thread-safety policies and explain how object monitors support cooperation between threads.

  1. How do StringBuffer and StringBuilder differ?

    Answer: Both represent mutable character sequences. StringBuffer synchronizes operations as required by its contract; StringBuilder does not provide that synchronization. For a builder confined to one thread, StringBuilder is normally the simpler choice. String itself is immutable.

    Thread-safe individual calls do not make a sequence of calls indivisible. If shared code checks a StringBuffer's length and then modifies it, another thread may intervene between those calls. Protect a required compound operation with a common lock. Also avoid concurrently changing a separate source sequence while appending it: the destination's lock does not automatically protect the source.

  2. What does Collections.synchronizedList return?

    Answer: It returns a synchronized wrapper backed by the supplied List. Operations through the wrapper coordinate using its monitor. Keep using the returned wrapper; modifying the original backing list directly bypasses that coordination.

    The wrapper does not make element objects immutable, and does not automatically combine multiple calls into a transaction. It also does not change whether the backing list supports an operation: wrapping an unmodifiable list does not make it mutable. Choose a concurrent collection when its semantics better match the workload.

  3. Are iteration and compound actions automatically safe on a synchronized list?

    Answer: No. Manually synchronize on the returned list while traversing it with an iterator, spliterator, or stream. Protect a check-then-act sequence with the same lock when the steps must be atomic. Synchronize on the wrapper, not on the original backing list.

    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;
    
    public class WrappedList {
        public static void main(String[] args) {
            List<String> names = Collections.synchronizedList(new ArrayList<>());
            synchronized (names) {
                if (!names.contains("Ada")) names.add("Ada");
                for (String name : names) System.out.println(name);
            }
        }
    }

    The output is Ada. All other accesses must respect the same wrapper policy. A snapshot copied while holding the lock can be processed afterward without keeping the lock held during lengthy work.

  4. Is a synchronized method the only way to coordinate shared state?

    Answer: No. A synchronized block can guard a selected region using a stable lock object. An instance synchronized method locks this; a static synchronized method locks its declaring Class object. These forms have existed since early Java, not just since Java 17.

    Other tools address specific needs: ReentrantLock offers explicit locking, atomic variables provide certain atomic updates, and BlockingQueue coordinates producers and consumers. Choose according to the invariant and required operations. Merely adding a synchronized modifier does not make accesses through other, incompatible paths safe.

  5. What is thread deadlock, and how can lock ordering prevent it?

    Answer: In a typical lock deadlock, threads form a cycle: each holds a resource another needs, and none can continue to release it. For example, one thread holds lock A and waits for B while another holds B and waits for A. Opposite acquisition order creates the possibility; it does not guarantee every execution deadlocks.

    Establish a consistent order whenever operations acquire multiple locks. Avoid calling unknown code while holding locks, because it may acquire others. Time-bounded explicit lock acquisition can support a recovery policy, but a timeout alone is not a substitute for designing that policy. Intrinsic monitor acquisition is not interruptible.

  6. What are wait, notify, and notifyAll used for?

    Answer: They coordinate threads around a condition protected by an object's monitor. A waiter tests the condition, calls wait while it is false, and tests again after waking. A producer changes the protected state and signals waiting threads.

    Notify selects one waiter, while notifyAll signals all waiters on that object. Neither method transfers the lock or stores a notification for a future waiter. Store the condition in shared state; do not rely on the notification itself as a durable message. Higher-level queues or latches often express the requirement more directly.

  7. What rules must monitor-waiting code follow?

    Answer: Own the same monitor used to protect the condition, test the condition in a loop, and handle interruption deliberately. Wait releases only its target monitor, then reacquires it before returning. It can return because of notification, a timeout, or a spurious wakeup, or throw InterruptedException.

    Changing the condition and signaling under the same lock prevents a gap between a waiter's test and its entry into the wait set. When a method cannot propagate InterruptedException, commonly restore the interrupt status and stop the operation rather than silently continuing as though the condition had been satisfied.

  8. Which class declares wait, notify, and notifyAll?

    Answer: java.lang.Object declares them as final instance methods. They operate on the monitor and wait set associated with the target object, rather than belonging exclusively to Thread or Runnable.

    Any suitable object can act as the monitor, but use a stable identity under a clear ownership policy. A private final Object lock is often easier to control than an interned string, boxed value, or publicly accessible object. The final reference keeps the chosen lock identity from being reassigned; it does not make arbitrary protected state immutable.

  9. How does a thread enter an object's wait set?

    Answer: It calls wait on that object while owning its monitor. Waiting releases all reentrant acquisitions of that monitor, but not locks held on other objects. On resumption, the acquisitions are restored before control returns to the caller.

    A thread merely blocked trying to enter synchronized code is not an Object.wait waiter. This distinction matters when diagnosing thread dumps. Notification removes a waiter from the wait set, but it may then contend to reacquire the monitor before it can recheck its condition.

  10. What happens if a monitor method is called without owning the right lock?

    Answer: Wait, notify, and notifyAll throw IllegalMonitorStateException when the current thread does not own the target object's monitor. Holding some other lock is insufficient. A synchronized method locks its receiver, so calling anotherObject.notifyAll inside it still requires acquiring anotherObject's monitor.

    The requirement concerns current ownership, not where the method call appears textually. A helper invoked while the caller owns the correct monitor can legally call notifyAll. Keeping the condition, lock, and signaling code together makes that requirement easier to review.

References: Synchronized list contract, StringBuffer, and Object monitor methods.


SEMrush Software