简体   繁体   中英

Java Threading Cyclic Notify() and Wait()

I'm having a code in Java where two objects wait and notify each other when one finished processing. I'll keep my code simple with the following example and assuming there are no syntax error (I just want you to know the logic is more important here rather than the syntax).

Assuming I have object A which is a thread having this pseudo code

class A is Thread {
    run() {
        while(true) {
            wait(); // wait for signal from B
            // then do something if signal received
            B.signal(); // let B know that we're done and wait again 
        }
    }
}

Then we have here B which is also a thread having this pseudo code

class B is Thread {
    run() {
        while(true) {
            // Do something
            A.signal(); // Let A know to continue processing
            wait(); // Wait for signal from A before doing something again
        }
    }
}

So as you can see there's a cycle. The problem is I am having a dead-lock and the reason here is because when A is finished processing, it signals B to work before it waits.. But by the time B is notified, there are chances that A still haven't reached the wait() code and B is already calling A.signal() and leads to a dead lock.

How do I properly solve this problem? The solution I have in mind is that when B is notified to work, I will let the thread of B sleep for a number of milliseconds but I don't think this is ever a good idea. Any help is appreciated, thanks in advance.

When you use notify() this should be associated with a state change.

When you use wait() this should be associated with a check for a state change.

In real code, you should only wait when you are waiting for something.

Note: wait() can wake spuriously, it doesn't mean notify() was called. As you noticed, notify() does nothing if nothing is wait()ing.


Instead of using this pattern, you can use a BlockingQueue to pass work/messages between threads. This has the wait/notify and the object containing work built in.

However, since you normally need a thread to do the work, there is an ExecutorService builtin to do this. This allows you to pass work to a pool of threads and collect the results.

In short, you should be using an ExecutorService.

如果A使用B的结果,那么您可以考虑使用BlockingQueue

As you can find described in the Javadoc , you need to put your wait calls inside a loop that checks for a condition. Otherwise, if you don't have a condition variable or expression that you can check, it is possible that you miss the notify because you were not waiting at that point.

Also, as others have pointed out, you need to hold the monitor of the object you are calling the wait or notify method on; that's what the synchronized keyword is for.

In the below fix, the condition is very simple; it's a variable called notified in classes A and B.

Also, to get this right, A and B need to know about each other. In your code you seemed to be invoking static methods; but the notify method needs to be called on an instance, so you need to keep references to the instances of A and B in B and A, respectively.

This fixes the problems:

class A is Thread {
    private B b;
    private boolean notified;

    public void run() {
        while(true) {
            synchronized(this) {
                while (!notified) {
                    try {
                        wait(); // wait for signal from B
                    } catch (InterruptedException e) {}
                }
                notified = false;
            }
            synchronized(b) {
                // then do something if signal received
                b.notified = true;
                b.notify(); // let B know that we're done and wait again 
            }
        }
    }
}

class B is Thread {
    private A a;
    private boolean notified;
    public void run() {
        while(true) {
            synchronized(a) {
                // Do something
                a.notified = true;
                a.notify(); // Let A know to continue processing
            }
            synchronized(this) {
                while (!notified) {
                    try {
                        wait(); // Wait for signal from A before doing something again
                    } catch (InterruptedException e) {}
                }
                notified = false;
            }
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM