3

我試圖解決一個Java同步問題。案例如下:古典Java同步問題(小心試試吧?)

有類人,他們想要配對。所以我有一個班級耦合,做配對。當一個人走進Coupling時,沒有人在等她,她開始等待。並等到有人出現,或者她感到無聊並離開(predifines計時器熄滅)。

如果她走了進來,發現有人在等她,他們會立即加上和他們交換電話號碼和各走各路。 (繼續與其他的信息執行。)

同一個人不能兩個人離開。

我保證,這不是一所大學的運動,我試圖作弊。:)我只是在一段時間沒有帶這樣做的東西,我在它有點生疏了。

這是我想出了起來,在第一,所以線程試圖設置的人,如果這也不行,它會得到錯誤的返回值。然後線程得到服務員。出於顯而易見的原因,這將不起作用(另一個線程可能會在這些調用之間),我將如何發出等待線程繼續。

這是我提到的代碼:

public class Coupling { 
    private static volatile Person waitingPerson = null; 

    public static synchronized Integer getWaitingPerson() { 
     Integer temp = waitingPerson; 
     waitingPerson = null; 
     return temp; 
    } 

    public static synchronized Boolean setWaitingPerson(Integer waitingPerson) { 
     if (waitingPerson == null){ 
      syncro.waitingPerson = waitingPerson; 
      return new Boolean(true); 
     } 
     else 
      return new Boolean(false); 
} 
+1

,在那裏做線程開始發揮作用?你有多少個線程,他們代表什麼等等? – 2011-04-01 16:37:14

+0

你爲什麼要同時使用'volatile'和'synchronized'? – Liv 2011-04-27 13:06:45

回答

0

一個簡單的解決方案

private final Exchanger<Person> waitingPerson = new Exchanger<Person>(); 

public Person getWaitingPerson(Person person, int timeoutMS) 
     throws InterruptedException, TimeoutException { 
    return waitingPerson.exchange(person, timeoutMS, TimeUnit.MILLISECONDS); 
} 

我認爲這些人沒有性偏好。 ;)

我承擔Person不能變成一個Integer

你談到等待,但你不隨地等待。 ;)

這將是更簡單的使用BlockingQueues來實現。

BTW:不要使用new Boolean(true)new Boolean(false)其實沒有理由不使用boolean

+0

這些是評論。不是一個真正的答案。 – Gray 2011-04-01 16:35:32

+0

在這種情況下,沒有理由使用'boolean'。但有時你需要使用AtomicBoolean。 – mre 2011-04-01 16:36:28

+0

@noob,作爲常量的返回值? – 2011-04-01 16:40:41

3

如果你真的只是想進入這個東西,這就是你們的榜樣的問題,想方設法把Java Concurrency in Practice副本,並找出你自己。它是一本很棒的書。

+0

聽起來像廣告。 – mre 2011-04-01 16:37:16

+0

@noob:呵呵,是的,你說得對。但偶然我真的很喜歡這本書。我發誓我沒有從這本書中獲利:)。 – Daniel 2011-04-01 16:38:59

1

怎麼不去做。粗糙的解決方案我最近在處理類似的測試後整理在一起。具有以下注釋中所述的問題。

public class Coupling implements Runnable { 

    private static Person waitingPerson = null; 

    private static Person secondPerson = null; 

    private static final Object waitLock = new Object(); 

    // Time out after a second 
    private static final Integer TIMEOUT = 1000; 

    public static Person getWaitingPerson(Person incoming) { 

     Person match = null; 

     // We're the second person in. 
     synchronized (waitLock) { 
      if (waitingPerson != null) { 

       // Get the person who is waiting 
       match = waitingPerson; 
       waitingPerson = null; 
       secondPerson = incoming; 

       // Let the other person know 
       waitLock.notify(); 
       return match; 
      } 
     } 

     // We're the first person in, wait for a second 
     synchronized(waitLock){ 
      waitingPerson = incoming; 
      try { 
       // Wait until someone is available 
       waitLock.wait(TIMEOUT); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 

      if(secondPerson != null){ 
       // Get the other person 
       match = secondPerson; 
       secondPerson = null; 
       return match; 
      } else { 
       // We timed out, reset 
       waitingPerson = null; 
       return null; 
      } 
     } 

    } 
} 

我有意忽略了Person類(不知道你的整數溫度是應該做的)的任何細節。

+2

這個模型有一些嚴重的競賽條件。如果足夠的線程在waitLock上排隊,那麼多個線程可以獲得相同的secondPerson。意識到這一點很重要,只是因爲有些事情已經通知並不意味着它接下來就會運行。它只是意味着它回到就緒隊列*後面*任何線程已經在那裏。 – Gray 2011-04-01 19:16:55

+2

非常好的一點,感謝評論。解釋了我前幾天用我的測試應用程序看到的一些奇怪行爲。我會把它留給別人的警告。 – 2011-04-01 19:47:49

+1

這是一個很好的頁面,我寫了一個關於競爭條件的頁面,以迴應我認爲是錯誤的stackoverflow答案。 http://256.com/gray/docs/misc/producer_consumer_race_conditions/ – Gray 2011-04-01 20:18:28

1

好吧,我裂傷您的問題一點點,提出解決方案,那麼你會告訴我,如果你滿意:-)

首先,你有沒有考慮使得「配對」操作異步?它的工作原理與此類似:

  1. 想要配對的人在Coupling線程中留下通知;
  2. 當第二個人離開「免費」通知時,Coupling線程會通知兩個人。

這將如何在代碼中工作?那麼,你需要兩件事:

  1. 一個支持累積許可證(信號!)的鎖;
  2. 一個線程安全的數據結構(好,以防萬一。這真的doesn'd 需要是線程安全的)

所以,每次一個人想要的時間進行配對,它會通知耦合線,將該人員添加到其「自由人員」列表中,並從信號中釋放一個許可證。另一方面,耦合線程不斷嘗試獲取信號量許可,一次兩個。由於只有當某人離開通知時纔會發放許可證,因此擁有兩個許可證意味着需要配對兩個人。

好吧,少說話,多點代碼。這裏的耦合線類:

import java.util.concurrent.BlockingQueue; 
import java.util.concurrent.LinkedBlockingQueue; 
import java.util.concurrent.Semaphore; 

public class Coupling implements Runnable { 
    private BlockingQueue<Person> peopleQueue; 

    private Semaphore semaphore; 

    public Coupling() { 
     this.peopleQueue = new LinkedBlockingQueue<Person>(); 
     this.semaphore = new Semaphore(0); 
    } 

    @Override 
    public void run() { 
     while(true) { 
      // Process incoming "free" events 

      try { 
       // Acquire permits for two people 
       this.semaphore.acquire(2); 

       Person pA = this.peopleQueue.poll(); 
       Person pB = this.peopleQueue.poll(); 

       pA.notifyPairing(pB); 
       pB.notifyPairing(pA); 
      } catch (InterruptedException e) { // This shouldn't really happen..? 
       break; 
      } 
     } 
    } 

    /** 
    * Invoked to notify that a Person is waiting for a coupling 
    * @param person 
    */ 
    public void notifyFreePerson(Person person) { 
     this.peopleQueue.offer(person); 
     this.semaphore.release(1); 
    } 
} 

而Person類:

public class Person { 
    public void notifyPairing(Person other) { 
     System.out.printf("I've been paired with %s!\n)", other); 
    } 
} 
在此練習
+0

我喜歡它。我也在考慮信號量,因爲我們需要一些計數。我可能天真地使notifyFreePerson方法同步,但我認爲它的工作方式很好。 – 2011-04-01 18:23:34