Java: How can a thread wait on multiple objects?

2019-02-05 22:40发布

A thread can use Object.wait() to block until another thread calls notify() or notifyAll() on that object.

But what if a thread wants to wait until one of multiple objects is signaled? For example, my thread must wait until either a) bytes become available to read from an InputStream or b) an item is added to an ArrayList.

How can the thread wait for either of these events to occur?

EDIT

This question deals with waiting for multiple threads to complete -- my case involves a thread waiting for one of many objects to be singnaled.

8条回答
爱情/是我丢掉的垃圾
2楼-- · 2019-02-05 22:45

You are in for a world of pain. Use a higher level abstraction, such as a blocking message queue, from which the thread can consume messages such as 'more bytes available' or 'item added'.

查看更多
戒情不戒烟
3楼-- · 2019-02-05 22:50

A thread cannot wait on more than one object at a time.

The wait() and notify() methods are object-specific. The wait() method suspends the current thread of execution, and tells the object to keep track of the suspended thread. The notify() method tells the object to wake up the suspended threads that it is currently keeping track of.

Useful link : Can a thread call wait() on two locks at once in Java (6) ?

查看更多
看我几分像从前
4楼-- · 2019-02-05 22:52

In order handle the termination of any thread from a given set without waiting for all of them to finish, a dedicated common Object (lastExited below) can be used as monitor (wait() and notify() in synchronized blocks). Further monitors are required for ensuring that at any time at most one thread is exiting (notifyExitMutex) and at most one thread is waiting for any thread to exit (waitAnyExitMonitor); thus the wait()/notify() pairs pertain always to different blocks.

Example (all process terminations are handled in the order the threads finished):

import java.util.Random;

public class ThreadMonitor {

    private final Runnable[] lastExited = { null };

    private final Object notifyExitMutex = new Object();
    public void startThread(final Runnable runnable) {
        (new Thread(new Runnable() { public void run() {
            try { runnable.run(); } catch (Throwable t) { }
            synchronized (notifyExitMutex) {
                synchronized (lastExited) {
                    while (true) {
                        try {
                            if (lastExited[0] != null) lastExited.wait();
                            lastExited[0] = runnable;
                            lastExited.notify();
                            return;
                        }
                        catch (InterruptedException e) { }
                    }
                }
            }
        }})).start();
    }

    private final Object waitAnyExitMutex = new Object();
    public Runnable waitAnyExit() throws InterruptedException {
        synchronized (waitAnyExitMutex) {
            synchronized (lastExited) {
                if (lastExited[0] == null) lastExited.wait();
                Runnable runnable = lastExited[0];
                lastExited[0] = null;
                lastExited.notify();
                return runnable;
            }
        }
    }

    private static Random random = new Random();
    public static void main(String[] args) throws InterruptedException {
        ThreadMonitor threadMonitor = new ThreadMonitor();

        int threadCount = 0;
        while (threadCount != 100) {
            Runnable runnable = new Runnable() { public void run() {
                try { Thread.sleep(1000 + random.nextInt(100)); }
                catch (InterruptedException e) { }
            }};
            threadMonitor.startThread(runnable);
            System.err.println(runnable + " started");
            threadCount++;
        }

        while (threadCount != 0) {
            Runnable runnable = threadMonitor.waitAnyExit();
            System.err.println(runnable + " exited");
            threadCount--;
        }
    }
}
查看更多
女痞
5楼-- · 2019-02-05 22:58

They could all use the same mutex. You consumer is waiting on that mutex, the both other notify on that mutex when the first can proceed.

查看更多
Summer. ? 凉城
6楼-- · 2019-02-05 23:00

Lock in both cases over the same object. Call in case a) or in case b) notify() on the same object.

查看更多
forever°为你锁心
7楼-- · 2019-02-05 23:00

It appears that in your case you're waiting for "notifications" from two different sources. You may not have to "wait" (as in normal java synchronized(object) object.wait()) on those two objects per se, but have them both talk to a queue or what not (as the other answers mention, some blocking collection like LinkedBlockingQueue).

If you really want to "wait" on two different java objects, you might be able to do so by applying some of the principles from this answer: https://stackoverflow.com/a/31885029/32453 (basically new up a thread each to do a wait on each of the objects you're waiting for, have them notify the main thread when the object itself is notified) but it might not be easy to manage the synchronized aspects.

查看更多
登录 后发表回答