多線程操作中為什么使用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();
    }
}

在set中使用了

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

在get中使用了

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

原因:

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

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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM