多线程操作中为什么使用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是否为空再继续,这样才是正确的操作;生产也是同一个道理。

 

posted @ 2015-03-10 13:28  黎明露珠  阅读(6245)  评论(0编辑  收藏  举报