2011-06-07 49 views
14

一個線程可以使用Object.wait()來阻塞,直到另一個線程在該對象上調用notify()notifyAll()Java:線程如何等待多個對象?

但是如果一個線程想等到多個對象之一發出信號呢?例如,我的線程必須等到或者 a)字節可用於從InputStream讀取或者b)將項目添加到ArrayList

線程如何等待這些事件中的任何一個發生?

編輯

This question涉及等待多個線程完成 - 我的案子涉及到一個線程等待許多對象之一被singnaled。

+0

也許你可以張貼一些源代碼,包括部分,其中「等待」被調用,這兩個部分在您通知應該發生。 – 2011-06-07 12:41:03

+1

*線程如何等待多個對象*,隊列。鎖定對象,放在隊列的一端並解鎖。另一方面:從其他方面彈出它,獲得鎖,處理,釋放鎖。沿着這些線路的東西。 – bestsss 2011-06-07 13:49:09

回答

4

線程無法一次在多個對象上等待。

wait()notify()方法是對象特定的。 wait()方法掛起當前執行線程,並通知對象跟蹤掛起的線程。方法notify()通知對象喚醒當前正在跟蹤的暫停線程。

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

1

將兩種情況都鎖定在同一個對象上。在情況a)時或在情況b)通知()時對同一個對象進行調用。

5

他們都可以使用相同的互斥鎖。您的消費者正在等待該互斥鎖,當第一個可以繼續時,另一個互斥體會通知該互斥鎖。

+1

@TimBüthe:我不認爲這是有效的,因爲當通知線程無法區分條件(a)和(b)時,即它不知道'InputStream'中是否有更多字節可用或一個項目已被添加到'ArrayList' – MarcoS 2011-06-07 12:46:52

+0

@MarcoS當然,它可以區分,但這不是同步的一部分。例如,您可以在某個地方,某條消息或類似地點持有狀態。 – 2011-06-07 12:52:59

+0

@MarcoS:好吧,你可以在通知等待線程之前設置一些標誌... – 2011-06-07 13:08:23

19

你是爲了一個痛苦的世界。使用更高級別的抽象,例如阻塞消息隊列,線程可以從中使用諸如「可用的更多字節」或「添加的項目」之類的消息。

+0

這就是它應該是... – bestsss 2011-06-07 13:49:52

2

您只能在一臺顯示器上等待。所以通知者必須通知這一個監視器。在這種低級同步中沒有其他方法。

2

小晚,但它是一個非常有趣的問題! 似乎你可以確實等待多個條件,具有相同的性能,並且不需要額外的線程;這只是一個定義問題的問題!我花時間在代碼的提交中寫下更詳細的解釋。通過請求我會提取抽象:

所以實際上等待多個對象,就等​​於在多個條件下等待。但下一步是將你的子條件合併成一個條件一個單一的條件。當條件的任何組件都會導致它成爲真時,您會翻轉一個布爾值,並通知鎖(就像任何其他等待通知條件一樣)。

我的做法

對於任何條件下,它只能導致兩個值(true和false)。價值如何產生是無關緊要的。在你的情況下,你的「功能條件」是當兩個值中的任何一個爲真時:(value_a || value_b)。我把這個「功能條件」稱爲「連結點」。如果你應用任何複雜條件(無論多麼複雜)的觀點,總會產生一個簡單的結果(真或假),那麼你真正要求的是「什麼會導致我的淨條件成爲現實?」 (假設邏輯是「等到真」)。因此,當一個線程導致你的條件組件變爲真(在你的情況下將value_a或value_b設置爲true),並且你知道它會導致你想要的網絡條件得到滿足,那麼你可以簡化你的接近古典(因爲它翻轉單個布爾標誌,並釋放一個鎖)。有了這個概念,你可以申請一個物體的座標的方法,幫助幫助你的整體邏輯清晰:

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. 
     } 

    } 


} 
+2

歡迎,@賈米。我確信這包含了一個很好的答案,但它不是StackExchange的答案。我建議你提取描述作爲你的答案,並根據需要用代碼片段來說明它,而不是要求讀者從你的代碼轉儲中挖出相關的位。順便說一句,我假設你有權在這裏發佈此代碼 - 是否正確?我只是問,因爲它看起來像一個課程或課程材料批發的類。 – 2015-07-22 16:48:41

+1

謝謝,是的,我做到了!這是我的第一篇文章,它整合了大約一個小時的純焦點(和一些哲學)。我幾乎考慮爲它製作一個圖書館,但它要密集。我相信它值得解釋 - 簡單來說 - 在JLS中似乎缺少組件的背後的天才。我會盡量壓縮它! – 2015-07-22 16:56:27

1

看來,你的情況是從兩個不同的源等待「通知」。您可能不需要在這兩個對象本身上「等待」(正如在java synchronized(object) object.wait()中那樣),而是讓它們都與隊列對話或不與其他對象(如其他答案所述,阻止像LinkedBlockingQueue這樣的集合)進行對話。

如果你真的想在兩個不同的java對象上「等待」,你可以通過應用這個答案的一些原則來做到這一點:https://stackoverflow.com/a/31885029/32453(基本上新建一個線程,每個線程在每個等待的對象,讓他們在通知對象本身時通知主線程),但管理同步的方面可能並不容易。

0

爲了從處理任何線程的終止一個給定的設定,而無需等待所有這些完成,專用公共對象(下面lastExited)可以被(在​​塊wait()notify())用作監視器。需要更多監視器來確保在任何時候至多有一個線程正在退出(notifyExitMutex),並且至多有一個線程正在等待任何線程退出(waitAnyExitMonitor);因此對總是與不同的塊相關。

例(全部過程終止按以下順序處理線程完成):

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--; 
     } 
    } 
}