2

This Q looks for verification and/or comments/opinions the following:

The example on Guarded Blocks is as follows:

public synchronized void guardedJoy() {
    // This guard only loops once for each special event, which may not
    // be the event we're waiting for.
    while(!joy) {
        try {
            wait();
        } catch (InterruptedException e) {}
    }
    System.out.println("Joy and efficiency have been achieved!");
}

The other end of this code-- the one setting joy properly is something like this:

public void setJoy2(TheClass t) {
    synchronized (t) {
        t.joy = true; 
        t.notifyAll();
    }
}

The above does the "signaling" on joy by the use of notify().

An alternative is managing this "signalling" by interrupt():

public void guardedJoy2() {
    // This guard only loops once for each special event, which may not
    // be the event we're waiting for.

    while(!joy) {
        synchronized(this) {
            try {
                wait();
            } catch (InterruptedException e) {}
        }
    }
    System.out.println("Joy and efficiency have been achieved!");
}

and the one setting joy and letting the thread waiting for it is:

public void setJoy2(TheClass t) {
    t.joy = true; 
    t.interrupt(); 
}

I'm looking to make a comparison between the two-- setJoy() and setJoy2().

First of all, guardedJoy2() above can "hear" both setJoy() and setJoy2() properly-- can see when joy is set and act the way it is expected to(?) How does guardedJoy2() compare to guardedJoy()? It achieves does the same thing as guardedJoy()-- i might be missing something, but i'm not seeing a difference in the outcome. The only difference is that guardedJoy2() released the lock of this from within the loop, and someone else acquire it before the method terminates for some unexpected results. Setting that aside (i.e., assuming that this is the only place where the use of joy and its side effects appear in the code), there's not difference between guardedJoy() and guardedJoy2()(?)

guardedJoy2() responds to both setJoy() and setJoy2(). It can "hear" from setJoy() when it is done, re-acquires its lock and go from there. And, it can "hear" from setJoy2()-- by receiving the interrupt and thus throwing InterruptedException to get out of wait(), that's also the end of synch'd statement, checks to see in while condition that joy is set and goes from there. If the interrupt was from "someone" else and not from the one setting joy, gets into the loop again the same way till joy is set.

When, wait() is invoked and thus the lock of this is released in guardedJoy2(), some other thread can get in by acquiring this lock and do things that are not supposed to be done till joy is set and guardedJoy2() is supposed to return properly. However, setting this aside (again, assuming this isn't an issue-- the only thing being looked for is seeing that message on the last line of guardedJoy2() on the console.) This-- setJoy2() can be preferable in cases where other things can be done on the object while it's getting its joy set and go from there (in setJoy2(), the thread setting joy doesn't have to have the lock of the object to interrupt it while setJoy() should have that lock to invoke notifyAll() on it).

How does guardedJoy2() & setJoy2() compare to guardedJoy() & setJoy() above?

TIA.

Roam
  • 4,831
  • 9
  • 43
  • 72

1 Answers1

0

I'm going of the assumption that you meant just notify() in your first setJoy rather than notifyAll().

First, it's important to note that if you're invoking interrupt() on an expression of type TheClass, then TheClass is a subclass of Thread. This goes against a number of -recommendations that state that you should use Runnable instances to encapsulate the logic to be run on a thread rather than subclassing the class Thread. The javadoc of Thread#join(int) also states

It is recommended that applications not use wait, notify, or notifyAll on Thread instances.

This is because some implementations of Java use those methods to handle thread logic behind the scenes. If you do not know that implementation logic and use these methods with Thread instances, you might get undesired behavior.

Then, and this might warrant some profiling, throwing (creating) an exception is an expensive operation, probably more so than removing a Thread from an object's wait set. What's more, exceptions should be used for exceptional conditions, not to guide your application logic.

I was going to say that your second example's synchronization order may be incorrect (assuming joy was not volatile) because the read in the guardedJoy2 loop might not see the write in setJoy2. However, the Java Language Specification states

If thread T1 interrupts thread T2, the interrupt by T1 synchronizes-with any point where any other thread (including T2) determines that T2 has been interrupted (by having an InterruptedException thrown or by invoking Thread.interrupted or Thread.isInterrupted)

So you still have visibility guarantees in place.

Community
  • 1
  • 1
Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724