多線程下建議使用while而不用if進行循環

在多線程操作中,我們常常會遇到需要先判斷信號量狀態是否就緒,然後執行後續操作的場景。這裏對狀態的判斷使用的是while而不是單線程下常用的if。 

以下示例展示了一個簡單的生產者-消費者模型:當隊列滿的時候,阻塞set;當隊列爲空的時候,阻塞get操作。

public class EventStorage {
    private int maxSize;
    private List<Date> storage;
    public EventStorage(){
        maxSize=10;
        storage=new LinkedList<>();
    }
    public synchronized void set(){
        while (storage.size()==maxSize){
            try {
                wait();
            } catch (InterruptedException e) {
            e.printStackTrace();
            }
        }
        storage.offer(new Date());
        System.out.printf("Set: %d",storage.size());
        notifyAll();
    }
    public synchronized void get(){
        while (storage.size()==0){
            try {
                wait();
            } catch (InterruptedException e) {
            e.printStackTrace();
            }
        }
        System.out.printf("Get: %d: %s",storage.
        size(),((LinkedList<?>)storage).poll());
        notifyAll();
    }
}
public class Producer implements Runnable {
    private EventStorage storage;
    public Producer(EventStorage storage){
        this.storage=storage;
    }
    @Override
    public void run() {
        for (int i=0; i<100; i++){
            storage.set();
        }
    }
 }
public class Consumer implements Runnable {
    private EventStorage storage;
    public Consumer(EventStorage storage){
        this.storage=storage;
    }
    @Override
    public void run() {
        for (int i=0; i<100; i++){
            storage.get();
        }
    }
}
public class Main {
    public static void main(String[] args) {
        EventStorage storage=new EventStorage();
        Producer producer=new Producer(storage);
        Thread thread1=new Thread(producer);
           Consumer consumer=new Consumer(storage);
         Thread thread2=new Thread(consumer);
           thread1.start();
        thread2.start();
    }
}

while (storage.size()==maxSize){
         try {
            wait();
       } catch (InterruptedException e) {
        e.printStackTrace();
       }
   }



while (storage.size()==0){
       try {
           wait();
       } catch (InterruptedException e) {
           e.printStackTrace();
     }
}

原因:

在線程中notify或者notifyAll會喚醒一個或多個線程,當線程被喚醒後,被喚醒的線程繼續執行阻塞後的操作。

這裏分析一下get操縱: 當某個線程得到鎖時storage爲空,此時它應該wait,下次被喚醒時(任意線程調用notify),storage可能還是空的。因爲有可能其他線程清空了storage。如果此時用的是if它將不再判斷storage是否爲空,直接繼續,這樣就引起了錯誤。但如果用while則每次被喚醒時都會先檢查storage是否爲空再繼續,這樣纔是正確的操作;生產也是同一個道理。



發佈了30 篇原創文章 · 獲贊 13 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章