2015-11-13 176 views
2

假設我有以下代碼,其中,一個線程產生平方和,而另一個線程打印它們把它們寫到一個緩衝器:兩個線程程序防止死鎖

import java.util.*; 

public class Something { 

public static Buffer buffer = new Buffer(); 

public static class Buffer { 

    private int[] buffer; 
    private static final int size = 10; 

    //Indexes for putting and taking element form buffer 
    private int in, out; 

    //Number of elements in buffer 
    private int k; 

    public Buffer() { 
     buffer = new int[size]; 
     in = 0; 
     out = 0; 
     k = 0; 
    } 

    public synchronized void put(int e) { 
     try { 
      while (k == buffer.length) { 
       wait(); 
      } 
     } catch (InterruptedException ex) { 
     } 
     buffer[in] = e; 
     k++; 
     in = ++in % size; 
     notifyAll(); 
    } 

    public synchronized int take() { 
     try { 
      while (k == 0) { 
       wait(); 
      } 
     } catch (InterruptedException ex) { 
     } 
     int e = buffer[out]; 
     buffer[out] = 0; 
     out = ++out % size; 
     k--; 
     notifyAll(); 
     return e; 
    } 

    public synchronized boolean notEmpty() { 
     return k != 0; 
    } 

} 

public static class Generator implements Runnable { 

    int limit; 

    public Generator(int lim) { 
     limit= lim; 
    } 

    @Override 
    public void run() { 
     for (int i = 1; i < limit; i++) { 
      buffer.put(i * i); 
     } 
    } 
} 

public static class Printer implements Runnable { 

    private Thread[] generators; 

    public Printer(Thread[] gen) { 
     generators = gen; 
    } 

    public synchronized boolean nobody() { 
     for (Thread th : generators) { 
      if (th.isAlive()) { 
       return false; 
      } 
     } 
     return true; 
    } 

    @Override 
    public void run() { 
     int x = 0; 
     while (!nobody() || buffer.notEmpty()) { 
      x = buffer.take(); 
      System.out.println(x); 
     } 
    } 
} 

public static void main(String[] args) throws InterruptedException { 
    Thread generator = new Thread(new Generator(69)); 
    Thread printer = new Thread(new Printer(new Thread[]{generator})); 

    generator.start(); 
    printer.start(); 

    generator.join(); 
    printer.join(); 
} 

}

發生器應該產生的平方直到達到某個限制(在這種情況下,限制= 69)。打印機應打印由Generator生成的所有值。緩衝區有點像環形緩衝區。用於放置()和取()元素的索引在緩衝區大小的範圍內循環。緩衝區有從緩衝區中放入和取出元素的方法。發生器線程如果已滿(即,沒有零元素;爲了精確起見零元素爲0),它不能將元素放入緩衝區中。打印機以這種方式工作:首先檢查是否有活動的生成器線程,然後檢查緩衝區是否只包含零個元素。如果這兩個條件都不成立,則打印機線程終止。

現在,解決問題。我總是打印從1到68的所有方塊,這是該程序的預期輸出。 但是,在所有數字輸出後非常罕見的情況下,我得到了一個僵局。多少次?那麼,也許在100個程序執行中有1個。我不得不繼續在NetBeans上點擊「F6」,以免陷入僵局。是的,我知道我可以測試這個只需將所有主代碼放入for循環。 相反,如果我在打印機的運行方法中註釋掉打印行,幾乎總是發生死鎖。在這裏:

 @Override 
     public void run() { 
     int x = 0; 
     while (!nobody() || buffer.notEmpty()) { 
      x = buffer.take(); 
      //System.out.println(x); 
     } 
    } 

我不希望這種行爲,因爲元素仍然會從緩衝和發電機所應被喚醒。

爲什麼會發生這種情況?我該如何解決它? 很抱歉,如果問題不夠清楚,我會盡可能地澄清它,如果需要的話。

+0

您可以確定哪個線程卡住(第一個)? – Turing85

+0

我確定它是打印機。它永遠在等待Generator將一些元素放入緩衝區。 – Schizo

+1

這個程序看起來非常複雜,它應該更復雜。爲什麼不使用隊列並刪除所有同步的塊。 – Leon

回答

0

我想我解決了這個問題。這是我得到的結果:有一個很短的時間,其中Generator線程仍然存在(即Thread.isAlive()將返回true),但Generator已將for -loop離開run()。如果Printer在此時間內在其run()內查詢其while條件,它將嘗試take()東西,那不是(並且永遠不會)。事實上,您可以驗證Generator總是結束,這意味着Printer一側的終止檢測出現故障。對於一個熱修復程序,你可以簡單地添加了一個幻常量爲Printer s,而條件:

@Override 
public void run() { 
    int x = 0; 
    int count = 0; 
    while (++count < 69) { 
     x = buffer.take(); 
     System.out.println(x); 
    } 
} 

對於一個乾淨終止檢測,你可以一些常見的標誌變量設置爲false,這表明該Generator已經完成的工作, Printer可以停止工作。但是這必須以同步的方式完成,這意味着Printer不允許查詢這個條件,而Generator在它的最後push之後,但是在它設置該公共標誌之前。

+1

實際上,nobody()上的同步根本沒有用,所以應該刪除以避免混淆。 – JimmyB