Java:线程如何在多个对象上等待?

时间:2011-06-07 12:36:17

标签: java multithreading wait notify

线程可以使用Object.wait()阻止,直到另一个线程调用该对象上的notify()notifyAll()

但是如果线程想要等到多个对象中的一个发出信号呢?例如,我的线程必须等到 a)字节可用于从InputStream读取或b)项目被添加到ArrayList

线程如何等待这些事件发生?

修改

This question处理等待多个线程完成 - 我的情况涉及一个线程等待许多对象中的一个被单独化。

8 个答案:

答案 0 :(得分:21)

你正处于痛苦的世界。使用更高级别的抽象,例如阻塞消息队列,线程可以从中使用消息,例如“更多字节可用”或“添加项目”。

答案 1 :(得分:6)

他们都可以使用相同的互斥锁。您的消费者正在等待该互斥锁,当第一个互斥锁可以继续时,两者都会通知该互斥锁。

答案 2 :(得分:5)

线程不能一次等待多个对象。

wait()notify()方法是特定于对象的。 wait()方法挂起当前执行线程,并告诉对象跟踪挂起的线程。 notify()方法告诉对象唤醒它当前正在跟踪的挂起线程。

有用的链接:Can a thread call wait() on two locks at once in Java (6) ?

答案 3 :(得分:3)

不太晚,但这是一个非常有趣的问题! 看起来你确实可以等待具有相同性能的多个条件,并且没有额外的线程;这只是定义问题的问题!我花时间在下面的代码提交中写了一个更详细的解释。根据要求,我将提取抽象:

所以实际上等待多个对象,就像等待多个条件一样。但下一步是将子条件合并为-net-条件 - 单条件 - 。当条件的任何组件导致它变为真时,你翻转一个布尔值,并通知锁(就像任何其他等待通知条件一样)。

我的方法

对于任何条件,它只能产生两个值(true和false)。如何产生这种价值是无关紧要的。在您的情况下,您的“功能条件”是两个值中的任何一个为真时:(value_a || value_b)。我将这种“功能状态”称为“Nexus-Point”。如果你应用任何复杂条件的观点 - 无论多么复杂 - 总是产生一个简单的结果(真或假),那么你真正要求的是“什么会导致我的净条件变为真实?” (假设逻辑是“等到真”)。因此,当一个线程导致你的条件的一个组件变为true时(在你的情况下将value_a或value_b设置为true),并且你知道它会导致你所需的-net-条件得到满足,那么你可以简化你的接近经典(因为它翻转一个布尔标志,并释放一个锁)。通过这个概念,您可以应用对象 - 纵坐标方法来帮助明确整体逻辑:

import java.util.HashSet;
import java.util.Set;

/**
 * The concept is that all control flow operation converge
 * to a single value: true or false. In the case of N
 * components in which create the resulting value, the
 * theory is the same. So I believe this is a matter of
 * perspective and permitting 'simple complexity'. for example:
 *
 * given the statement:
 *      while(condition_a || condition_b || ...) { ... }
 *
 * you could think of it as:
 *      let C = the boolean -resulting- value of (condition_a || condition_b || ...),
 *      so C = (condition_a || condition_b || ...);
 *
 * Now if we were to we-write the statement, in lamest-terms:
 *      while(C) { ... }
 *
 * Now if you recognise this form, you'll notice its just the standard
 * syntax for any control-flow statement?
 *
 *      while(condition_is_not_met) {
 *          synchronized (lock_for_condition) {
 *              lock_for_condition.wait();
 *            }
 *      }
 *
 * So in theory, even if the said condition was evolved from some
 * complex form, it should be treated as nothing more then if it
 * was in the simplest form. So whenever a component of the condition,
 * in which cause the net-condition (resulting value of the complex
 * condition) to be met, you would simply flip the boolean and notify
 * a lock to un-park whoever is waiting on it. Just like any standard
 * fashion.
 *
 * So thinking ahead, if you were to think of your given condition as a
 * function whos result is true or false, and takes the parameters of the states
 * in which its comprised of (  f(...) = (state_a || state_b && state_c), for example )
 * then you would recognize "If I enter this state, in which this I know would
 * cause that condition/lock to become true, I should just flip the switch switch,
 * and notify".
 *
 * So in your example, your 'functional condition' is:
 *      while(!state_a && !state_b) {
 *          wait until state a or state b is false ....
 *      }
 *
 * So armed with this mindset, using a simple/assertive form,
 * you would recognize that the overall question:
 * -> What would cause my condition to be true? : if  state_a is true OR state_b is true
 * Ok... So, that means: When state_a or state_b turn true, my overall condition is met!
 * So... I can just simplify this thing:
 *
 *      boolean net_condition = ...
 *      final Object lock = new Lock();
 *
 *      void await() {
 *          synchronized(lock) {
 *              while(!net_condition) {
 *                  lock.wait();
 *              }
 *           }
 *       }
 *
 * Almighty, so whenever I turn state_a true, I should just flip and notify
 * the net_condition!
 *
 *
 *
 * Now for a more expanded form of the SAME THING, just more direct and clear:
 *
 * @author Jamie Meisch
 */
public class Main {


    /**
     *
     * The equivalent if one was to "Wait for one of many condition/lock to
     * be notify me when met" :
     *
     *      synchronized(lock_a,lock_b,lock_c) {
     *          while(!condition_a || !condition_b || !condition_c) {
     *              condition_a.wait();
     *              condition_b.wait();
     *              condition_c.wait();
     *          }
     *      }
     *
     */
    public static void main(String... args) {

        OrNexusLock lock = new OrNexusLock();
        // The workers register themselves as their own variable as part of the overall condition,
        // in which is defined by the OrNuxusLock custom-implement. Which will be true if any of
        // the given variables are true
        SpinningWarrior warrior_a = new SpinningWarrior(lock,1000,5);
        SpinningWarrior warrior_b = new SpinningWarrior(lock,1000,20);
        SpinningWarrior warrior_c = new SpinningWarrior(lock,1000,50);

        new Thread(warrior_a).start();
        new Thread(warrior_b).start();
        new Thread(warrior_c).start();

        // So... if any one of these guys reaches 1000, stop waiting:
        // ^ As defined by our implement within the OrNexusLock


        try {
            System.out.println("Waiting for one of these guys to be done, or two, or all! does not matter, whoever comes first");
            lock.await();
            System.out.println("WIN: " + warrior_a.value() + ":" + warrior_b.value() + ":" + warrior_c.value());
        } catch (InterruptedException ignored) {
        }

    }


    // For those not using Java 8 :)
    public interface Condition {
        boolean value();
    }

    /**
     * A variable in which the net locks 'condition function'
     * uses to determine its overall -net- state.
     */
    public static class Variable {

        private final Object lock;
        private final Condition con;

        private Variable(Object lock, Condition con) {
            this.lock = lock;
            this.con  = con;
        }

        public boolean value() {
            return con.value();
        }

        //When the value of the condition changes, this should be called
        public void valueChanged() {
            synchronized (lock) {
                lock.notifyAll();
            }
        }

    }



    /**
     *
     * The lock has a custom function in which it derives its resulting
     * -overall- state (met, or not met). The form of the function does
     * not matter, but it only has boolean variables to work from. The
     * conditions are in their abstract form (a boolean value, how ever
     * that sub-condition is met). It's important to retain the theory
     * that complex conditions yeild a simple result. So expressing a
     * complex statement such as ( field * 5 > 20 ) results in a simple
     * true or false value condition/variable is what this approach is
     * about. Also by centerializing the overal logic, its much more
     * clear then the raw -simplest- form (listed above), and just
     * as fast!
     */
    public static abstract class NexusLock {
        private final Object lock;

        public NexusLock() {
            lock = new Object();
        }

        //Any complex condition you can fathom!
        //Plus I prefer it be consolidated into a nexus point,
        // and not asserted by assertive wake-ups
        protected abstract boolean stateFunction();

        protected Variable newVariable(Condition condition) {
            return new Variable(lock, condition);
        }

        //Wait for the overall condition to be met
        public void await() throws InterruptedException {
            synchronized (lock) {
                while (!stateFunction()) {
                    lock.wait();
                }
            }
        }

    }

    // A implement in which any variable must be true
    public static class OrNexusLock extends NexusLock {


        private final Set<Variable> vars = new HashSet<>();

        public OrNexusLock() {
        }


        public Variable newVar(Condition con) {
            Variable var = newVariable(con);
            vars.add(var); //register it as a general component of or net condition       // We should notify the thread since our functional-condition has changed/evolved:
            synchronized (lock) { lock.notifyAll(); }
            return var;
        }

        @Override
        public boolean stateFunction() { //Our condition for this lock
            // if any variable is true: if(var_a || var_b || var_c || ...)

            for(Variable var : vars) {
                if(var.value() == true) return true;
            }
            return false;
        }

    }

    //increments a value with delay, the condition is met when the provided count is reached
    private static class SpinningWarrior implements Runnable, Condition {

        private final int count;
        private final long delay;
        private final Variable var;

        private int tick = 0;

        public SpinningWarrior(OrNexusLock lock, int count, long delay) {
            this.var   = lock.newVar(this);
            this.count = count; //What to count to?
            this.delay = delay;
        }

        @Override
        public void run() {
            while (state_value==false) { //We're still counting up!
                tick++;
                chkState();
                try {
                    Thread.sleep(delay);
                } catch (InterruptedException ignored) {
                    break;
                }
            }
        }

        /**
         * Though redundant value-change-notification are OK,
         * its best to prevent them. As such its made clear to
         * that we will ever change state once.
         */
        private boolean state_value = false;
        private void chkState() {
            if(state_value ==true) return;
            if(tick >= count) {
                state_value = true;
                var.valueChanged(); //Our value has changed
            }
        }

        @Override
        public boolean value() {
            return state_value; //We could compute our condition in here, but for example sake.
        }

    }


}

答案 4 :(得分:2)

在两种情况下锁定同一对象。在案例a)或案例b)中调用同一对象的notify()。

答案 5 :(得分:2)

您只能在一台显示器上等待。因此通知者必须通知这一个监视器。这种低级同步没有其他办法。

答案 6 :(得分:2)

在您的情况下,您似乎正在等待来自两个不同来源的“通知”。您可能不必在这两个对象本身上“等待”(如在普通的java synchronized(object) object.wait()中),但让它们都与队列对话或者不对齐(正如其他答案所提到的,一些阻塞集合,如LinkedBlockingQueue)

如果你真的想在两个不同的java对象上“等待”,你可以通过应用这个答案中的一些原则来做到这一点:https://stackoverflow.com/a/31885029/32453(基本上每个新线程都要做一个等待在您正在等待的每个对象上,让它们在通知对象本身时通知主线程)但是管理同步方面可能并不容易。

答案 7 :(得分:0)

为了处理来自给定集合 的任何线程的终止,而不等待所有线程完成 ,一个专用的公共对象(lastExited下面)可用作wait()块中的监视器(notify()synchronized)。需要进一步的监视器以确保在任何时候最多一个线程退出(notifyExitMutex)并且最多一个线程正在等待任何线程退出(waitAnyExitMonitor);因此wait() / notify()对总是属于不同的块。

示例(按照线程完成的顺序处理所有进程终止):

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--;
        }
    }
}
相关问题