23

スレッドは、別のスレッドが呼び出すまで、またはそのオブジェクトObject.wait()をブロックするために使用できます。notify()notifyAll()

しかし、スレッドが複数のオブジェクトの1つが通知されるまで待機したい場合はどうなりますか?たとえば、私のスレッドは、a)バイトがaから読み取ることができるようになるInputStream、 b)アイテムがに追加されるまで待機する必要がありますArrayList

スレッドはこれらのイベントのいずれかが発生するのをどのように待つことができますか?

編集

この質問は、複数のスレッドが完了するのを待つことを扱っています。私の場合は、多くのオブジェクトの1つが単一化されるのを待つスレッドが関係しています。

4

8 に答える 8

24

あなたは苦痛の世界にいます。ブロッキングメッセージキューなどのより高いレベルの抽象化を使用します。この抽象化から、スレッドは「使用可能なバイト数が増えました」や「アイテムが追加されました」などのメッセージを消費できます。

于 2011-06-07T12:40:19.300 に答える
6

それらはすべて同じミューテックスを使用できます。あなたの消費者はそのミューテックスを待っています、他の両方は最初が進むことができるときにそのミューテックスについて通知します。

于 2011-06-07T12:39:35.603 に答える
5

スレッドは、一度に複数のオブジェクトを待機することはできません。

およびメソッドwait()notify()オブジェクト固有です。このwait()メソッドは、現在の実行スレッドを一時停止し、一時停止されたスレッドを追跡するようにオブジェクトに指示します。このnotify()メソッドは、現在追跡している中断されたスレッドをウェイクアップするようにオブジェクトに指示します。

便利なリンク:Java(6)でスレッドが2つのロックで同時にwait()を呼び出すことはできますか?

于 2011-06-07T12:49:30.623 に答える
4

少し遅れましたが、とても興味深い質問です!確かに、同じパフォーマンスで、余分なスレッドがなく、複数の条件を待つことができるようです。問題を定義するだけです!私は時間をかけて、以下のコードのコミット内でより詳細な説明を書きました。リクエストにより、抽象化を抽出します。

したがって、実際には、複数のオブジェクトを待機することは、複数の条件を待機することと同じです。ただし、次のステップは、サブ条件を-net-条件-単一条件-にマージすることです。また、条件のいずれかのコンポーネントによってそれがtrueになる場合は、ブール値を反転し、ロックに通知します(他の待機通知条件と同様)。

私のアプローチ

どのような条件でも、結果は2つの値(trueとfalse)になります。その価値がどのように生み出されるかは関係ありません。あなたの場合、あなたの「機能的条件」は、2つの値のいずれかが真である場合です:(value_a || value_b)。私はこれを「機能状態」を「ネクサスポイント」と呼んでいます。どんなに複雑な条件でも(どんなに複雑でも)常に単純な結果(真または偽)が得られるという観点を適用すると、本当に求めているのは「ネット条件が真になる原因は何ですか?」です。(ロジックが「真になるまで待つ」と仮定します)。したがって、スレッドによって条件のコンポーネントが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.
        }

    }


}
于 2015-07-22T16:34:45.030 に答える
2

どちらの場合も、同じオブジェクトをロックします。同じオブジェクトでケースa)またはケースb)notify()を呼び出します。

于 2011-06-07T12:38:34.990 に答える
2

1台のモニターでのみ待機できます。したがって、通知者はこの1台のモニターに通知する必要があります。この低レベルの同期には他の方法はありません。

于 2011-06-07T12:43:57.473 に答える
2

あなたの場合、2つの異なるソースからの「通知」を待っているようです。これらの2つのオブジェクト自体を(通常のjavaのように)「待機」する必要はないかもしれませんが、synchronized(object) object.wait()両方をキューと通信させるか、そうでないか(他の回答で言及されているように、LinkedBlockingQueueなどのブロッキングコレクション)。

2つの異なるJavaオブジェクトを本当に「待機」したい場合は、次の回答の原則のいくつかを適用することでそうできる可能性があります:https ://stackoverflow.com/a/31885029/32453 (基本的にスレッドを新しくするそれぞれが待機している各オブジェクトを待機し、オブジェクト自体が通知されたときにメインスレッドに通知するようにします)が、同期されたアスペクトを管理するのは簡単ではない場合があります。

于 2015-08-07T19:28:12.810 に答える
0

すべてのスレッドが終了するのを待たずに、特定のセットからのスレッドの終了を処理するために、専用の共通オブジェクト(lastExited以下)をモニターとして(wait()およびブロックnotify()で)使用できます。synchronizedいつでも最大で1つのスレッドが終了し(notifyExitMutex)、最大で1つのスレッドが任意のスレッドが終了するのを待機している()ことを確認するには、さらにモニターが必要です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--;
        }
    }
}
于 2016-02-05T12:38:00.937 に答える