Java线程中虚假唤醒问题

741 阅读2分钟

先定义资源类 :包含资源数量,消费方法,生产方法

当资源为0时,生产者生产资源

当资源为1时,消费者消费资源

//定义Resource作为线程需要的资源
public class Resource {
    //当前资源的数量
    int num=0;
    //当前资源的上限
    int size=1;
    //消费资源
    public synchronized void remove(){
        if(num==0){
            try {
                System.out.println("消费者等待");
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        num--;
        System.out.println("消费者线程为:"+Thread.currentThread().getName()+"资源数量"+num);
        notifyAll();
    }
    //生产资源
    public synchronized void put(){
        if(num==size){
            try {
                System.out.println("生产者等待");
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        num++;
        System.out.println("生产者线程为:"+Thread.currentThread().getName()+"资源数量"+num);
        notifyAll();
    }

}

创建消费者线程

//定义Consumer使用remove消费资源
public class Consumer implements Runnable {
    private Resource resource;
    public Consumer(Resource resource) {
        this.resource = resource;
    }
    @Override
    public void run() {
        for(int i=0;i<10;i++){
            resource.remove();
        }
    }
}

创建生产者线程

//定义Producer使用put生产资源
public class Producer implements Runnable {
    private Resource resource;
    public Producer(Resource resource) {
        this.resource = resource;
    }
    @Override
    public void run() {
        for(int i=0;i<10;i++){
            resource.put();
        }
    }
}

执行线程

public static void main(String[] args) {
    Resource resource = new Resource();
    Consumer consumer = new Consumer(resource);
    Producer producer = new Producer(resource);
    new Thread(consumer).start();
    new Thread(producer).start();
}

可以发现:线程执行情况正常

image.png

但是当我们分别有两个消费者和生产者时,线程执行就出现了异常

public static void main(String[] args) {
    Resource resource = new Resource();
    Consumer consumer = new Consumer(resource);
    Producer producer = new Producer(resource);
    new Thread(producer,"生产者1").start();
    new Thread(producer,"生产者2").start();
    new Thread(consumer,"消费者1").start();
    new Thread(consumer,"消费者2").start();
}

image.png

这就是虚假唤醒问题

当我们的线程执行了start,此时线程都进入就绪状态

image.png

生产者1获取cpu执行权,变为运行状态,执行其中run方法,资源数+1

image.png

接着生产者1和生产者2先后获取cpu执行权,因为资源数为1,线程阻塞

image.png

消费者获取cpu执行权,资源数-1,并且会唤醒阻塞的线程

image.png

这时候就变成最初的状态

image.png

当生产者再次执行,是从刚才阻塞的地方继续运行,就不用再判断资源的数量是否为0了

image.png

image.png

出现虚假唤醒的原因是从阻塞态到就绪态再到运行态没有进行判断

我们可以每次得到操作权时都进行判断,将if判断改成while判断即可

此时执行情况正常

image.png