You have discovered an odd effect which is not going to be on the
test. It turns out that in
Java's
Thread implementation, whenever a Thread finishes running, it calls its own notifyAll() method. This allows any other threads that are calling join() to receive a notification and resume running. Because Reader and Calculator both extend Thread directly, they have this behavior, which means they call notifyAll() as soon as their run method ends. Since the notifyAll() shown in the code runs just
before run() ends, notifyAll() basically gets called twice in a row. When you comment out one of those calls, the second remains.
Here's an alternate version implementing Runnable rather than extending Thread. This way Calculator and Reader don't inherit the unexpected behavior of Thread. If you comment out the notifyAll(), you will see that no reader ever returns from the wait().
I also added a Thread.sleep() inside Calculator's run() method, to avoid the problem discussed in the next few pages ("Using wait() in a loop"). Without this, you may not always see all three Readers finish.