Can I call two standby threads on one monitor a dead end?

Two threads are waiting on one monitor, for example, if one thread causes a "block" wait, and the other thread that acquired the monitor also waits before the first thread is notified. Now both threads are waiting, but no one is receiving notifications. What would I call this situation? Can this be called a dead end?

Edit: It is assumed that they are the only two streams, and it is not possible to receive notification from other sources from them. Update: I just created the situation I was talking about. The following code snippet works fine in most cases when the changer thread starts before the listener thread. However, when I start the listener in front of the changer, the program simply freezes after printing two lines (one from the changer and one from the listener thread). Will the situation when I call the listener in front of the changer be called a dead end?

package demo; public class ProducerConsumer { public static int SAMPLE_INT = 0; public static void main(String[] args) { PC pc = new PC(); Thread changer = new Thread(new Runnable() { public void run(){ try { pc.producer(); } catch (InterruptedException e) { e.printStackTrace(); } } }); Thread listener = new Thread(new Runnable(){ public void run() { try { pc.consumer(); } catch (InterruptedException e) { e.printStackTrace(); } } }); changer.start(); listener.start(); } } class PC { Object lock = new Object(); public void producer() throws InterruptedException { synchronized(this){ for (int i=0; i<5; i++){ ProducerConsumer.SAMPLE_INT++; System.out.println("Changed value of int to: " + ProducerConsumer.SAMPLE_INT); wait(); notify(); } } } public void consumer() throws InterruptedException{ synchronized(this){ for (int i=0; i<5; i++){ System.out.println("Receieved Change: " + ProducerConsumer.SAMPLE_INT); notify(); wait(); } } } } 


Conclusion when the changer starts before the listener:
Int value changed: 1
Received change: 1 | Int value changed: 2
Change received: 2
Int value changed: 3
Change received: 3
Int value changed: 4
Change received: 4
Int value changed: 5
Change received: 5
The program ends.

Conclusion when the listener starts before the changer:
Received change: 0 | Int value changed: 1
The program does not end.

Thanks.

+6
source share
7 answers

If these are only two threads involved (for example, with access to a monitor), then yes. If there are additional threads that can access the monitor and unlock it, then no.

Keep in mind, however, that you are talking about two topics - a monitor is usually a mutex in terms of threads. But wait is something related to a condition variable, which, although it requires the use of a mutex, performs the more subtle task of intentionally blocking a thread based on a condition that one thread passes to another, with a warning known as false awakenings.

+6
source

This is not a dead end , because there is a way out of this situation - if the third thread calls notify() or notifyAll() , then the two previous waiting threads will return to the ready state.

The deadlock usually cannot be resolved inside the application itself and requires a restart.

That is why I would not call your situation a dead end.

There are two other terms that describe thread coordination problems:

Livelock and Starvation

Here are the exact definitions of LoveLock and Starvation - Starvation and LiveLock

This is not LiveLock, but because streams do not act with each other.

Your situation is probably closest to the term Fasting , but not exactly hunger. Starvation is when a thread waits for a resource that has been accepted for a very long time. In your case, the resource is a lock on the object, and it will never be received again. So your best shot is something like Endless Hunger .

I would call this a producer-consumer error, because the wait-notify mechanism describes and controls the coordination of flows for the producer-consumer template, and this approach (waiting without notification) is simply a developer error or method skips.

+5
source

A deadlock basically means that the thread holds the lock (first lock) and then wants to get another lock (second lock), which it can never get, since the second lock is held by another thread that wants to get the first lock, This can also happen in thread chain, for example, when Thread1 has LockA, Thread2 has LockB, Thread3 has LockC, and they are waiting for locks held by other threads (for example, Thread1 wants LockB, Thread2 needs LockC and Thread3 for LockA). In this case, none of the threads can be continued.

Only such scenarios are called deadlocks; the thread holding the lock waits for another lock, which will never be acquired unless it releases its lock.

This wait call on the lock object technically releases the lock held by the thread.

So, in order to answer your question, I do not think that you can name the scenario that you mentioned in the question as a dead end.

+1
source

For this scenario, it is better to use the wait (timeout) method rather than wait (). Even if there is no notification from another thread, it will release the lock after a waiting period

0
source

@ S.Doe With editing your question, the situation is a deadlock because there are 2 threads, and both are in the waiting queue and do not execute any code on it. The third thread also does not mean that no thread is even in runnable / running state. This is a dead end.

0
source

You cannot control this code, which thread will first get the lock. If the consumer first acquires, his call for notification () does not spill the thread, and he waits for the lock to remain. Now the manufacturer acquires a lock, produces and waits. This is a common race condition problem, and you need to control which thread should start execution first (Manufacturer in this scenario). You can use CyclicBarrier to control the sequence of thread calls.

0
source

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) 
0
source

All Articles