I'm doing my first concurrency exercices. I can't figure out why the following works when my objects subclass Thread but don't when they implement Runnable:
Works:
Doesn't work (seems that notify() never "gets through"):
In your example, when you inherit from the Thread class, both threads use the *same* object for notifications -- the thread object of the waiter.
In your example, when you use runnables, the two threads are *not* using the same object. The notify thread is using the wait thread object, while the wait thread is using the runnable object embedded in the wait thread object.
I see the light, thanks. In my greenhornness, I wasn't distinguishing between the thread object itself and the potentially different Runnable object containing the thread's task. A mild criticism of Eckel's chapter on Concurrency (Thinking in Java, 3rd Edition), which is my introduction to the topic: Since he has an expressed preference for subclassing Thread rather than using Runnable (since, where appropriate, the Thread subclass can be an inner class and therefore not prevent the outer class from inheriting from something else), he doesn't have many examples using Runnable. Seems to me my confusion would be common among beginners and should be addressed in an introductory text (nevertheless, I find the Eckel book awesome). I'm off to Borders this afternoon to buy your 3rd edition on Threads. For those following this "thread," here is a version of "Runnables" that works:
Alan Mehio
Ranch Hand
Joined: Apr 04, 2005
Posts: 70
posted
0
Please correct me if I am wrong but I executed your code after I inserted some flag messages and I have noticed the execution passes the notify in both casess so there is no difference if we extend Thread or if we implement Runnable. please take a look at the code (Eclipse 3.1 and Window 2000 J2SDK 1.4.2_07)
A- extends Thread
<result> Waiter is holding the lock now inside the synchronized block Waiter will wait Notifier will sleep Notifier waked up from a sleep Notifier holding the lock before notify call Notifier holding the lock after notify call Waiter is back to run Waiter IS NOT holding the lock now outside the synchronized block Waiter is awakened Waiter is holding the lock now inside the synchronized block Waiter will wait NotifierIS NOT holding the lock outside the synchronized block Notifier will sleep Notifier waked up from a sleep Notifier holding the lock before notify call Notifier holding the lock after notify call Waiter is back to run Waiter IS NOT holding the lock now outside the synchronized block Waiter is awakened Waiter is holding the lock now inside the synchronized block ...etc </result>
<code>
class Waiter extends Thread { Waiter(String name){ super(name); }
public void run(){ while(true){ synchronized(this){ if (Thread.currentThread().holdsLock(this)) System.out.println(Thread.currentThread().getName() + " is holding the lock now inside the synchronized block"); try { System.out.println(Thread.currentThread().getName() + " will wait"); wait(); System.out.println(Thread.currentThread().getName() + " is back to run"); }catch(InterruptedException ie){ throw new RuntimeException(); } } if (Thread.currentThread().holdsLock(this)) System.out.println(Thread.currentThread().getName() + " is holding the lock now outside the synchronized block"); else System.out.println(Thread.currentThread().getName() + " IS NOT holding the lock now outside the synchronized block");
System.out.println(" Waiter is awakened");
} } }
class Notifier extends Thread { private Thread ot;
Notifier(Thread otherThread, String name){ super(name); ot = otherThread; }
public void run(){ while(true){ try { System.out.println(Thread.currentThread().getName() + " will sleep"); Thread.sleep(100); System.out.println(Thread.currentThread().getName() + " waked up from a sleep"); } catch(InterruptedException ie) { throw new RuntimeException(); } synchronized(ot){ if (Thread.currentThread().holdsLock(ot)) System.out.println(Thread.currentThread().getName() + " holding the lock before notify call"); ot.notify(); if (Thread.currentThread().holdsLock(ot)) System.out.println(Thread.currentThread().getName() + " holding the lock after notify call"); else System.out.println(Thread.currentThread().getName() + " IS NOT holding the lock after notify call"); } if (Thread.currentThread().holdsLock(ot)) System.out.println(Thread.currentThread().getName() + " holding the lock outside the synchronized block"); else System.out.println(Thread.currentThread().getName() + "IS NOT holding the lock outside the synchronized block");
} } }
public class Threads {
public static void main(String[] args) { Thread t1 = new Waiter("Waiter"); Thread t2 = new Notifier(t1, "Notifier"); t1.start(); t2.start(); } }
</code>
B- Runnable
<result> Waiter is holding the lock now inside the synchronized block Waiter will wait Notifier will sleep Notifier waked up from a sleep Notifier holding the lock before notify call Notifier holding the lock after notify call Waiter is back to run Waiter IS NOT holding the lock now outside the synchronized block Waiter is awakened Waiter is holding the lock now inside the synchronized block Waiter will wait NotifierIS NOT holding outside the synchronized block Notifier will sleep Notifier waked up from a sleep Notifier holding the lock before notify call Notifier holding the lock after notify call Waiter is back to run Waiter IS NOT holding the lock now outside the synchronized block Waiter is awakened Waiter is holding the lock now inside the synchronized block Waiter will wait NotifierIS NOT holding outside the synchronized block Notifier will sleep
... etc
</result>
<code> class Waiter implements Runnable {
public void run(){ while(true){ synchronized(this){ if (Thread.currentThread().holdsLock(this)) System.out.println(Thread.currentThread().getName() + " is holding the lock now inside the synchronized block"); try { System.out.println(Thread.currentThread().getName() + " will wait"); wait(); System.out.println(Thread.currentThread().getName() + " is back to run"); }catch(InterruptedException ie){ throw new RuntimeException(); } } if (Thread.currentThread().holdsLock(this)) System.out.println(Thread.currentThread().getName() + " is holding the lock now outside the synchronized block"); else System.out.println(Thread.currentThread().getName() + " IS NOT holding the lock now outside the synchronized block");
System.out.println(" Waiter is awakened");
} } }
class Notifier implements Runnable { private Runnable waiter;
Notifier(Runnable waiter){
this.waiter = waiter; }
public void run(){ while(true){ try { System.out.println(Thread.currentThread().getName() + " will sleep"); Thread.sleep(100); System.out.println(Thread.currentThread().getName() + " waked up from a sleep"); } catch(InterruptedException ie) { throw new RuntimeException(); } synchronized(waiter){ if (Thread.currentThread().holdsLock(waiter)) System.out.println(Thread.currentThread().getName() + " holding the lock before notify call"); waiter.notify(); if (Thread.currentThread().holdsLock(waiter)) System.out.println(Thread.currentThread().getName() + " holding the lock after notify call"); else System.out.println(Thread.currentThread().getName() + " IS NOT holding the lock after notify call"); } if (Thread.currentThread().holdsLock(waiter)) System.out.println(Thread.currentThread().getName() + "holding outside the synchronized block"); else System.out.println(Thread.currentThread().getName() + "IS NOT holding outside the synchronized block");
} } }
public class Runnables {
public static void main(String[] args) { Waiter waiter = new Waiter(); Thread t1 = new Thread(waiter, "Waiter"); Thread t2 = new Thread(new Notifier(waiter), "Notifier"); t1.start(); t2.start(); } }