2014-01-18 62 views
-1

我正在學習線程,所以我想編寫一個程序,它有兩種類型的線程:一種寫隨機數字,另一種檢查當前數字是否與某個特定數字匹配。線程從Numbers類調用write()和read(int)方法。爲了讓事情更清楚,我想我的主要程序是這樣的:Java線程按順序

Numbers n = new Numbers(); 
new WritingThread(n); 
new ReadingThread(n,3); 
new ReadingThread(n,5); 

所以輸出會是這樣的:

2 
7 
3 !!! MATCH !!! 
8 
5 !!! MATCH !!! 
1 
... 

的事情是,線程不按順序執行。我想先執行WritingThread,然後執行所有的ReadingThreads。因爲這樣一個新的隨機數字將被寫入,並且只有一個線程將有機會檢查數字是否匹配。下面是代碼:

類數

public class Numbers { 
int number; 
boolean written = false; 

public synchronized void write() { 
    while (written) 
     try { 
      wait(); 
     } catch (InterruptedException e1) { 
      e1.printStackTrace(); 
     } 

    number = (int) (Math.random() * 10); 
    System.out.print("\n" + number); 
    written = true; 
    notifyAll(); 
} 

public synchronized void check(int n) { 
    while (!written) 
     try { 
      wait(); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 
    System.out.print(" Reading thread: " + Thread.currentThread().getName()); 
    if (n == number) 
     System.out.print(" !!! MATCH !!! "); 
    notify(); 
    written = false; 
} 
} 

類WritingThread

public class WritingThread extends Thread { 
    Numbers n; 
    WritingThread(Numbers n){ 
     this.n = n; 
     start(); 
    } 

    public void run(){ 
     while(true){ 
      n.write(); 
     } 
    } 
} 

類ReadingThread

public class ReadingThread extends Thread{ 
Numbers n; 
int number; 
public ReadingThread(Numbers n, int number){ 
    this.n = n; 
    this.number = number; 
    start(); 
} 

public void run(){ 
    while(true){ 
     n.check(number); 
    } 
} 
} 

和輸出:

3 Reading thread: Thread-2 
3 Reading thread: Thread-1 !!! MATCH !!! 
0 Reading thread: Thread-2 
5 Reading thread: Thread-1 
0 Reading thread: Thread-2 
0 Reading thread: Thread-1 
5 Reading thread: Thread-2 !!! MATCH !!! 
8 Reading thread: Thread-1 

我知道我能讓它有數字數組來檢查一個線程,但我很好奇它如何可以做到這樣。謝謝。

+2

你在問什麼?嘗試打印匹配,或者閱讀[java.util.concurrent.Future](http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Future.html)。 –

+0

我這麼認爲,但如何實現它,在哪裏? – tuks

+0

@ElliottFrisch注意到輸出中的第4行。它應該是匹配的,但它不是,因爲線程1正在檢查匹配,線程2沒有得到機會 – tuks

回答

2

讓我們從你的例子開始。你有兩個消費者和一個布爾標誌。仔細考慮邏輯。讓我們打電話給我們的三個線程W,C1C2

  • W後5
  • W設置標誌true
  • W發送notifyAll
  • C2清醒
  • C1清醒
  • C2獲取時鐘
  • C1
  • C2沒有匹配
  • C2通知
  • W清醒
  • W blocks
  • C2釋放鎖
  • C1獲取鎖
  • 標誌爲假,C1等待(版本監測)
  • 國旗是假的,C2等待(版本監控)
  • GOTO開始

這只是一個如果在此代碼可以樂趣很多可能的方式。任何時候都需要獲取鎖,對於所有的線程和等待它的線程都是免費的,只有一個線程可以獲得鎖。該線程將檢查設置的值並重置標誌。如果該線程不是該值所用的線程,它仍然被消耗。

它應該是相當明顯的,你有種族危害。您正在爲兩個消費者線程使用單個隊列。每個消費者線程都在爭奪隊列。您的隊列是線程安全的,因爲在任何時候,只有一個線程可以從中讀取單個項目,但它會導致競爭風險,因爲每個消費者線程都希望成爲唯一一個讀取線程的線程。如果錯誤的線程讀取該項目,則另一個線程無法看到它。

解決此問題的唯一方法是每個線程有一個隊列。生產者將相同的項目放入每個消費者線程的專用隊列中,並且每個消費者線程從其隊列中取出項目並讀取它們。

下面是一個使用示例的ExecutorSerivce

public static void main(String[] args) throws Exception { 

    final class Consumer implements Runnable { 

     private final BlockingQueue<Integer> q = new LinkedBlockingDeque<>(); 
     private final int search; 

     public Consumer(final int search) { 
      this.search = search; 
     } 

     @Override 
     public void run() { 
      while (true) { 
       try { 
        if (q.take() == search) { 
         System.out.println("Found magic number."); 
        } 
       } catch (InterruptedException ex) { 
        return; 
       } 
      } 
     } 

     public Queue<Integer> getQ() { 
      return q; 
     } 
    } 

    final class Producer implements Runnable { 

     final Random r = new Random(); 
     final Iterable<Queue<Integer>> qs; 

     public Producer(final Iterable<Queue<Integer>> qs) { 
      this.qs = qs; 
     } 

     @Override 
     public void run() { 
      while (true) { 
       final int i = r.nextInt(); 
       for (final Queue<Integer> q : qs) { 
        q.offer(i); 
       } 
      } 
     } 
    } 

    final int numConsumers = 5; 
    final Collection<Queue<Integer>> qs = new LinkedList<>(); 
    final ExecutorService es = Executors.newCachedThreadPool(); 
    for (int i = 0; i < numConsumers; ++i) { 
     final Consumer c = new Consumer(i); 
     qs.add(c.getQ()); 
     es.submit(c); 
    } 
    es.submit(new Producer(qs)); 
} 

你很可能爲Random.nextInt()用這個例子來獲得很少的點擊率。如果您希望獲得更多匹配,請通過調用Random.nextInt(int max)來減少生成的隨機數的範圍,該數字會生成數字[0, max)

正如你所看到的,每個Consumer都有一個要檢查的項目隊列,並且它使用BlockingQueue API來等待新項目。 Producer依次將相同的項目放入Consumer的每個隊列中。

+0

非常感謝你的努力 – tuks