/* Java implementation of the Win32 API's "Event" synchronization mechanism. */ class MSEvent { boolean manual; // manual or autoresetevent boolean open = false; // false means closed int clients = 0; public MSEvent(boolean m) {manual=m;} public synchronized void lock() throws InterruptedException { clients++; while(!open) wait(); // wait will exit monitor if (!manual) open = false; // close door behind you, be rude! clients--; if (clients==0 && manual) notify(); // notifies resetEvent caller } // lock public synchronized void setEvent() throws InterruptedException { open = true; notifyAll(); // wake threads up to recheck the open var } public synchronized void resetEvent() throws InterruptedException { if (!manual) return; // do nothing if it's autoreset int originalPriority = Thread.currentThread().getPriority(); while (clients>0) { Thread.currentThread().setPriority(Thread.MAX_PRIORITY); wait(); } Thread.currentThread().setPriority(originalPriority); open = false; } } // MSEvent /* Note that in order to implement this device precisely we needed to manipulate the priority class of the threads. The default priority of threads is Thread.NORM_PRIORITY. So we can give a thread a higher priority if we choose to. This is because Java defines no specific behavior as to which thread is woken up after a call to notify() or notifyAll(). In Ada, a call to "requeue" guarantees that the thread will get to enter a protected type monitor before any external thread call to an entry is accepted and queued. In an Ada protected type, the evaluation of the "when" guard conditions is automatic a the start of the call, and every time some thread exits an entry. This all have to be done manually inside Java. Another interesting point to note is the use of "while" instead of "if" in the lock() method. When setEvent() wakes up all threads by calling notifyAll(), the threads will "wake" up in the sense that they will re-queue themselfs outside the monitor. They will still execute the synchronized methods one by one. If one of them gets in and locks the gate as it goes through (because of the autoreset option), then other threads would need to block themselfs again. We could use notify() instead of notifyAll() in lock(), but that would not work for the manual reset option. A key concept to understand is the difference between a thread that is RUNNNING, WAITING and READY. A thread that calls wait is WAITING for a notify signal. When it's notified, it goes to the READY state. That means it wants to run but that doesn't mean it'll run immediately. It still have to wait for the currently RUNNING thread to relinquish the monitor (by exiting the synchronized method). So for example, what if I have code that looks like: synchronized void f() { notify(); System.out.println("when will this get printed?"); System.out.flush(); } The answer is that it'll get printed immediately. The thread that it has "notified" goes to the READY state, but it cannot run until you have exited the monitor by completing the synchronized method call. */