I think Krasimir and Andy gave the right answers. I wanted to give additional proof (view) that this situation is technically not a dead end.
And this is due to the fact that the Java thread dump in this program does not report that it is a dead end . Although it is possible that the Java thread dump is incorrect, we can assume that if there is a dead end, the thread dump will do this fine.
So, I ran your program with listener (consumer) running before changer (producer) in textual order , and, like you, I got a hang. At the moment I have a stream dump (there are various ways to do this by looking at your IDE, this is the easiest). The relevant parts of the stream dump are here [1].
If it was a dead end, a thread dump would clearly say this (something like One Java level dead end found ). But since this is not so, we can be sure that this is not a dead end.
Looking at a thread dump (which is a stack dump for each thread) is still very instructive. You can see that both producer flows and consumers first blocked the lock <0x000000076abca250> (entered on the monitor), as was supposed in the lock ... line. And with curiosity (or not), both of them are waiting for the same castle (as the waiting on ... line suggests waiting on ... )!
This is where the subtlety of inputting the stream wait stream is located . In order for a thread to wait on a lock, it must first abandon the same lock.
This is exactly what consumer#wait does (the second thread in the dump below): gives an exclusive lock, so some other thread can really grab it and hopefully wake it up via notify .
Well, consumer now out of contention for this lock. Note that a call to notify , already made by consumer before wait , may or may not go astray. If so, then we have this situation hanging that we are trying to solve.
At the same time, the flow of manufacturers also began to work (perhaps a little later). Since the consumer "refused" the lock in order to wait on it, the manufacturer grabs the lock, and now it performs wait . As a consumer, now he is waiting on the same lock in the same hope that someone will come and wake him up!
Again, technically, none of the two threads blocked the lock. Since both threads are waiting, they abandoned the lock, and if some other thread comes (too bad, not in this case!), Their hope can be successful. But in this case, the program will hang forever, because nothing else can change the situation (the JVM-hanging has its own reasons in threads that are not daemons, but this is another detail). Thus, wait-notify are low-level primitives in Java that are used to associate threads, and their proper use is done through a so-called condition variable. This is similar to threads recognizing that they work with a shared resource and interact with each other in any boundary cases (for example, the general queue is empty filled) through these primitives.
If the thread actually contains the L1 lock (i.e., it is executed in the synchronized section), and suddenly it requires the capture of another L2 lock to perform its task and at the same time, the other thread, while holding L2, searches for the L1 lock, then a deadlock occurs. Since it is not, it is not a dead end.
Having said that, I have a number of suggestions for improving this code. As Joshua Bloch says, “sample code should be exemplary”:
- Delete the line
Object lock = new Object(); from the PC class. This is not practical. You DO NOT block it. You are blocking the monitor associated with the PC instance: PC pc = new PC(); . - Try renaming
SAMPLE_INT to something like count in public static int SAMPLE_INT = 0; and make it volatile . This does not guarantee that changes made to this variable are visible to another thread. - There is no need to define
SAMPLE_INT in ProducerConsumer , define it in PC . - As someone suggested, consider using CountdownLatch when starting threads.
[1] Dump stream (I used the package name tmp ):
2016-02-23 15:02:49 Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.74-b02 mixed mode): "DestroyJavaVM" #13 prio=5 os_prio=31 tid=0x00007f80b381c800 nid=0x1303 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "Thread-0" #11 prio=5 os_prio=31 tid=0x00007f80b480b000 nid=0x5903 in Object.wait() [0x00000001294e5000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x000000076abca250> (a tmp.PC) at java.lang.Object.wait(Object.java:502) at tmp.PC.producer(ProducerConsumer.java:44) - locked <0x000000076abca250> (a tmp.PC) at tmp.ProducerConsumer$1.run(ProducerConsumer.java:14) at java.lang.Thread.run(Thread.java:745) "Thread-1" #12 prio=5 os_prio=31 tid=0x00007f80b6801000 nid=0x5703 in Object.wait() [0x00000001293e2000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x000000076abca250> (a tmp.PC) at java.lang.Object.wait(Object.java:502) at tmp.PC.consumer(ProducerConsumer.java:55) - locked <0x000000076abca250> (a tmp.PC) at tmp.ProducerConsumer$2.run(ProducerConsumer.java:24) at java.lang.Thread.run(Thread.java:745)