2013-02-26 58 views
1

你好,我有問題了解這裏提供的代碼,這段代碼展示了一個如何正確實現wait()和notify()到線程的例子。在這些線程中,這個布爾值用於什麼?

下面的代碼:

class Q { 

    int n;  
    boolean valueSet = false; 

    synchronized int get() { 
    if(!valueSet) 
    try { 
     wait(); 
    } catch(InterruptedException e) { 
     System.out.println("InterruptedException caught"); 
    } 
    System.out.println("Got: " + n); 
    valueSet = false; 
    notify(); 
    return n;  
    } 

    synchronized void put(int n) { 
    if(valueSet) 
    try { 
     wait(); 
    } catch(InterruptedException e) { 
     System.out.println("InterruptedException caught"); 
    } 
    this.n = n; 
    valueSet = true; 
    System.out.println("Put: " + n); 
    notify(); 
    } 
} 

class Producer implements Runnable { 
    Q q; 
    Producer(Q q) { 
    this.q = q; 
    new Thread(this, "Producer").start(); 
    } 
    public void run() { 
    int i = 0; 
    while(true) { 
     q.put(i++); 
    } 
    }  
} 

class Consumer implements Runnable { 
    Q q; 
    Consumer(Q q) { 
    this.q = q; 
    new Thread(this, "Consumer").start(); 
    } 
    public void run() { 
    while(true) { 
     q.get(); 
    } 
    } 
} 

class PCFixed { 
    public static void main(String args[]) { 
     Q q = new Q(); 
     new Producer(q); 
     new Consumer(q); 
     System.out.println("Press Control-C to stop."); 
    } 
    } 

我也很難理解這裏的布爾的使用情況,如果布爾變量撐地,該代碼將正確打印。

但是,如果我拿走布爾值,那麼它只會打印「按Control-C停止」。這是爲什麼?

爲什麼布爾這麼重要,它的用法是什麼?

謝謝。

回答

1

Q類爲一個存儲在n中的單個整數實現了一個容器。該容器用於將該值從Producer傳遞到Consumer。由於容器一次只能保存一個值,所以如果容器已滿或未滿,則ProducerConsumer都必須知道某種方式。布爾值valueSet就是這個指標。

如果設置爲true,容器已滿,因此Producer必須等到它被清空再重新填充之前。同樣,如果valueSet爲假,那麼Consumer可能不會嘗試檢索Q實例的內容,直到找到要檢索的內容爲止。

通過移除布爾(和其狀態的測試),你把處於等待狀態都ProducerConsumer線程的通知(這將可能永遠不會發生,因爲只有他們能夠產生它的代碼),因此唯一出現的信息是主線程中的信息。

很重要的一點:由Freedom_Ben在自己answer暗示,此代碼的工作,因爲這兩個getput方法制成synchronised,這意味着它們將阻止所有其他線程試圖在執行過程中通過​​電話訪問對象,使這些呼籲在原則上相互對立。這一點很重要,因爲它幾乎可以保證在原子上對valueSetn進行讀寫。如果沒有兩種方法設置的屬性,則put的通知可能在Consumer已檢查valueSet之後但在它調用wait之前發生。根據通知機制(*)的實施情況,這可能導致Consumer錯過通知並進入等待狀態,即使Q中存在值。 通過這些方法的​​屬性,我們確信這些調用將按預期運行。


(*)的waitnotifiy代碼可以以兩種方式來實現:

  • 快速的方法是讓notify簡單地檢查線程是否爲wait ing,如果是,則將其喚醒,否則不做任何事情。這種情況導致競爭條件沒有正確​​方法調用。

  • 更正確的方法是使用在0初始化專用信號燈,分別化名信號量的notifiywaitincrement(又名release)和decrement(又名acquire)操作。

1

布爾值的名稱是valueSet。如果屬實,則表示已設置了一個值。當它是假的,這意味着一個值沒有被設置。將布爾視爲一個標誌,當它爲真時,有數據需要使用(標誌爲上),當它爲假時,不會消耗數據(標誌爲低)。

只有標誌爲false,生產者線程纔會設置一個值。如果這是真的,它將等待消費者通知。

消費者線程將只讀取標誌爲true的值。如果它是假的,它將等待生產者通知。

您是否有權訪問並使用調試器?瀏覽這兩個主題並查看它們如何與彼此交互可能會對您有所幫助。如果您以前沒有使用調試器,多線程可能不是理想的學習場景。

1

它看起來像使用布爾值來避免等待,如果最後的值尚未處理。例如,在put方法中,如果valueSet爲true,那麼我們將跳過wait(),因爲這意味着get()自上次更新this.n以來尚未運行。如果我們每次都等待(),而不管布爾值是多少,那麼死鎖的機率是相當不錯的,兩個線程都在等待,沒有人會通知。

這是我不喜歡將synchronized關鍵字應用到方法的原因。對於哪個對象被用作互斥體可能會引起混淆。我更喜歡這種風格,因爲更清楚哪些資源正在等待。我也發現這種風格通過在不需要同步的互斥體下工作來阻止懶惰。儘管如此,所有個人偏好:

void get(int n){ 
    synchronized(this){ 
     // do the work 
    } 
} 

void put(int n){ 
    synchronized(this){ 
     // do the work 
    } 
}