Review condition waiting, timed waits, exception handling, and the distinction between a Runnable task and an object's monitor.
Answer: Wait lets a thread suspend while a shared condition is false, releasing the condition's monitor so another thread can change the state. Notify signals one thread waiting on that same object. The notified thread must reacquire the monitor and recheck the condition.
Notification is not a queued message and does not itself encode the condition. If no thread is waiting, a notify call has no saved wakeup effect. A correctly guarded state variable lets a later caller see that waiting is unnecessary, avoiding dependence on which thread reaches the code first.
Answer: It throws IllegalMonitorStateException. The thread must own the monitor of the exact target object. Being inside unrelated synchronized code or owning an explicit ReentrantLock does not satisfy that requirement.
Use synchronized(target) around both the condition check and target.wait. With an explicit Lock, use its associated Condition and the await/signal protocol instead. IllegalMonitorStateException is unchecked; InterruptedException from a valid wait is checked and needs a separate handling decision.
Answer: No. Assuming the caller owns a's monitor, the call releases it and requests a wait of up to 2,000 milliseconds. Notification, interruption, or a spurious wakeup can end waiting earlier. After the timeout expires, reacquiring the monitor and being scheduled can delay the actual return.
Therefore two seconds is not a strict upper bound on the entire method call. Recheck the condition after every return. For a total time budget across repeated waits, calculate remaining time using System.nanoTime rather than restarting the full timeout each time. A zero timeout means an indefinite Object.wait, not a no-op.
Answer: No. The notifying thread continues to own the monitor. A notified waiter competes to reacquire it only after it becomes available. Ordinary exit from a synchronized region releases that acquisition; Object.wait temporarily releases its own target monitor.
Thread.sleep does not release held monitors. Join is a request to wait for thread termination, not a general operation for releasing unrelated application locks. Do not sleep or join while holding a lock needed by the thread whose progress you depend on; doing so can prevent that progress.
Answer: Notify chooses one waiter from the target object's wait set. NotifyAll signals all of them, but they still reacquire the monitor one at a time. It does not let them all execute the protected region concurrently.
NotifyAll is often appropriate when several different conditions share one monitor and selecting an arbitrary waiter could signal the wrong condition. Each awakened thread must loop on its own predicate. Neither operation changes application state automatically; the signaling code must make the state change first under the same lock.
Answer: The selection is unspecified. Do not assume it chooses the oldest waiter, the highest-priority thread, or the next thread needed by your application. Selection also does not guarantee that this thread is the next one to acquire the monitor.
If correctness requires a specific recipient, represent that requirement explicitly, for example with separate queues or conditions. A single monitor with a notify call is not a routing protocol. Correctness must survive any selection allowed by the API.
Answer: It is an unchecked subclass of IllegalArgumentException indicating an operation inappropriate for a thread's lifecycle state. Calling start twice on one Thread is a common example. A terminated thread cannot be restarted.
public class StartOnce {
public static void main(String[] args) throws InterruptedException {
Thread worker = new Thread(() -> {});
worker.start();
worker.join();
try {
worker.start();
} catch (IllegalThreadStateException expected) {
System.out.println("Cannot restart");
}
}
}
The output is Cannot restart. This deliberate demonstration catches the exception; normal application code should manage lifecycle correctly rather than use failed start calls as its coordination mechanism.
Answer: Wait does not evaluate an application condition. Your code must do that. Spurious wakeups are allowed, and even after a real notification another thread may consume or reverse the condition before the waiter reacquires the lock.
The usual shape is while (!ready) monitor.wait(); while holding monitor. The producer updates ready using the same monitor. An if statement tests only once and can allow execution to proceed when the predicate is still false. Interruption and timeout require their own policy rather than being treated as proof of readiness.
Answer: Runnable declares void run();, implicitly public and abstract. It has no parameters, no return value, and no checked throws clause. A lambda such as () -> System.out.println("Done") can implement it.
Calling run directly executes an ordinary method call on the current thread. Passing the task to a started Thread or an appropriate executor requests execution according to that mechanism's policy. Use Callable when a task should return a result or declare checked exceptions.
Answer: Monitor coordination belongs to the object whose state is being guarded, rather than to one particular Thread object. Object supplies the final instance methods so an ordinary lock object can have a wait set used by multiple threads.
Arrays and other objects can technically be monitors, but choose a dedicated stable object or a clearly documented receiver. Avoid using a Thread instance as a general application monitor, because thread lifecycle operations may use it internally. A dedicated monitor keeps application conditions separate from lifecycle management.
References: Object.wait and notification, Thread lifecycle, and Runnable.