2017-07-20 41 views
0

這是我的代碼和輸出是不同的任何時候,我運行的代碼。有時候,所有三個讀者會得到通知,並輸出結果是:notifyAll的()不會通知所有線程

等待計算...

等待計算...

等待計算...

成品

總是:4950Thread-1

總是:4950Thread-2

總計爲:4950Thread-0

有時候只有兩個或一個讀者會收到通知。 有什麼問題?

class Reader extends Thread { 
    Calculator c; 

    public Reader(Calculator calc) { 
     c = calc; 
    } 

    public void run() { 
     synchronized (c) { 
      try { 
       System.out.println("Waiting for calculation..."); 
       c.wait(); 
      } catch (InterruptedException e) { 
      } 
      System.out.println("Total is: " + c.total +Thread.currentThread().getName()); 
     } 
    } 

    public static void main(String[] args) { 
     Calculator calculator = new Calculator(); 
     new Reader(calculator).start(); 
     new Reader(calculator).start(); 
     new Reader(calculator).start(); 
     new Thread(calculator).start(); 
    } 
} 

class Calculator implements Runnable { 
    int total; 

    public void run() { 
     synchronized (this) { 
      for (int i = 0; i < 100; i++) { 
       total += i; 
      } 
      System.out.println("Finished"); 
      notifyAll(); 
     } 
    } 
} 

根據meta post,這個問題被聲稱是重複的,但這兩個被重複使用的「重複」都不適用。 How to use wait and notify in Java?提醒用戶,如果你真正希望等待對同一個對象,你必須對該對象進行同步。但是這個解決方案已經在做這個。 Java: notify() vs. notifyAll() all over again提醒用戶這甚至進一步問題notifynotifyAll之間的差異。

+0

適用於MacOS/JDK 1.8上的我! – fhossfel

+1

計算器可以在Reader開始等待之前通知。 – Grief

+1

順便說一句,延伸線程是不明智的。 – efekctive

回答

1

我能夠重現問題 - 基本上,如果你有太快的計算機,或是壞的調度,這是可能的計算器來完成的讀者有機會和同步之前等待。

C:\文件\ J>時的Java讀者
等待計算...
等待計算...
成品
等待計算...
總計爲:4950Thread-2
總計爲:4950Thread-0

爲了防止這種情況,你應該確認所有讀者都在進行計算之前準備就緒。

C:\文件\ J>時的Java讀者 等待計算... 等待讀者......目前1 等待計算... 等待計算... 成品 總計爲:4950Thread -1 總計爲:4950Thread-2 總計爲:4950Thread-0

這裏是我的代碼

import java.util.concurrent.atomic.AtomicInteger; // corsiKa added import 
class Reader extends Thread { 

    static class Calculator implements Runnable { 
     int total; 
     AtomicInteger readers = new AtomicInteger(0); // corsiKa added atomicinteger 

     public void run() { 
      // corsiKa added while 
      while(readers.get() < 3) { 
       System.out.println("Waiting for readers... currently " + readers.get()); 
       try { Thread.sleep(100); } catch(InterruptedException e) { } 
      } 
      synchronized (this) { 
       for (int i = 0; i < 100; i++) { 
        total += i; 
       } 
       System.out.println("Finished"); 
       notifyAll(); 
      } 
     } 
    } 

    Calculator c; 

    public Reader(Calculator calc) { 
     c = calc; 
    } 

    public void run() { 
     synchronized (c) { 
      try { 
       c.readers.incrementAndGet(); // corsiKa added increment 
       System.out.println("Waiting for calculation..."); 
       c.wait(); 
      } catch (InterruptedException e) { 
      } 
      System.out.println("Total is: " + c.total +Thread.currentThread().getName()); 
     } 
    } 

    public static void main(String[] args) { 
     Calculator calculator = new Calculator(); 
     new Reader(calculator).start(); 
     new Reader(calculator).start(); 
     new Reader(calculator).start(); 
     new Thread(calculator).start(); 
    } 
} 
+0

謝謝,我得到了這個問題。我應該在運行方法 – navid

+0

的同步塊之前添加睡眠方法正是!我正要上傳一個例子。 – corsiKa

+3

使用sleep來解決這個問題是一個非常糟糕的主意。這減少了發生問題的可能性,但沒有解決程序中出現的同步問題。 –

0

與您的代碼的問題是,該線程的開始訂單沒有定義。直觀的預期行爲是讀者首先開始,然後是計算器。這正是您在上面顯示的日誌中發生的情況。

但也有其他的交錯可能的。撥打Thread.start對起始順序沒有任何保證。假設兩個讀者先開始,然後是計算器,然後是最後一個讀者。在這種情況下,計算器可以在第三個閱讀器之前進入關鍵部分。然後,在第三個閱讀器執行等待呼叫之前,發生計算器的notifyAll調用。因此,第三位讀者可能會永遠等待,因爲在鎖定對象上不會發生notifynotifyAll的其他呼叫。

一個可能的解決問題的方法是使用CountDownLatch它允許你讓計算器等到所有三個讀者都準備好了。

0

讓你的主要方法檢查其他閱讀器的狀態。

public static void main(String[] args) { 
    Calculator calculator = new Calculator(); 
    Reader read1 = new Reader(calculator); 
    read1.start(); 
    Reader read2 = new Reader(calculator); 
    read2.start(); 
    Reader read3 = new Reader(calculator); 
    read3.start(); 

    while(read1.getState() != Thread.State.WAITING && 
      read2.getState() != Thread.State.WAITING && 
      read3.getState() != Thread.State.WAITING){ 
     try { 
      Thread.sleep(100); 
     } catch (InterruptedException e) { 
      // TODO Auto-generated catch block 
      e.printStackTrace(); 
     } 
    } 

    new Thread(calculator).start(); 
} 

這將確保你絕對有所有的線程在等待,而每次得到相同的結果。

相關問題