5-2阻塞队列的应用-生产者消费者

5-2阻塞队列的应用-生产者消费者,第1张

5-2阻塞队列的应用-生产者消费者 1虚假唤醒

什么是虚假唤醒?

参考网上的一个问题和回答:

问题:java多线程中虚假唤醒的原因都有什么?

网上查到的定义是说,线程在没有调用过notify()和notifyAll()的情况下醒来,是虚假唤醒。这是什么原因会导致的呢?

回答:虚假唤醒(spurious wakeup)是一个表象,即在多处理器的系统下发出wait的程序有可能在没有notify唤醒的情形下苏醒继续执行。以运行在linux的hotspot虚拟机上的java程序为例,wait方法在jvm执行时实质是调用了底层pthread_cond_wait/pthread_cond_timedwait函数,挂起等待条件变量来达到线程间同步通信的效果,而底层wait函数在设计之初为了不减慢条件变量 *** 作的效率并没有去保证每次唤醒都是由notify触发,而是把这个任务交由上层应用去实现,即使用者需要定义一个循环去判断是否条件真能满足程序继续运行的需求,当然这样的实现也可以避免因为设计缺陷导致程序异常唤醒的问题。

下面复现一下场景

class ProConShop{
    int num;
    
    public synchronized void incrementProduct() throws InterruptedException {
        //1判断商店是否有商品
        if (num!=0){//虚假唤醒
       //while (num!=0){
            this.wait();
        }
        //2生产商品
        num++;
        System.out.println(Thread.currentThread().getName()+"放入商品,商品号为"+num);
        //3通知消费者
        this.notifyAll();
    }

    
    public synchronized void decrementProduct() throws InterruptedException {
        //1判断商店是否有商品
        if (num==0){
            //while (num==0){
            this.wait();
        }
        //2消费商品
        System.out.println(Thread.currentThread().getName()+"号顾客购买商品,商品号为"+num);
        num--;
        //3通知生产者
        this.notifyAll();
    }
}

public class ProductTest {
    public static void main(String[] args) {
        ProConShop shop = new ProConShop();
        for (int i = 1; i <= 5; i++) {
            new Thread(()->{
                try {
                    shop.incrementProduct();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            },"店员").start();
        }
        for (int i = 1; i <= 5; i++) {
            new Thread(()->{
                try {
                    shop.decrementProduct();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            },i+"").start();
        }
    }
}

从该地方起 num!=0 线程没有wait

该文章00多线程5.2线程通信案例:生产者消费者代码存在虚假唤醒问题

从网上找的一篇解读

解决方法:生产者消费者的方法判断语句if换成while

运行效果为:

2生产者消费者案例Lock方式

生产者消费者案例synchronized方式实现方式在 1虚假唤醒 

2.1采用lock方式代替synchronized
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();

package com.nie.juc;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

class ProConShop{

    int num = 0;
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
    
    public void incrementProduct() throws InterruptedException {
        lock.lock();
        try {
            //1判断商店是否有商品
            while (num!=0){
                this.wait();
//                condition.await();//等待
            }
            //2生产商品
            num++;
            System.out.println(Thread.currentThread().getName()+"放入商品,商品号为"+num);
            //3通知消费者
            this.notifyAll();
//            condition.signalAll();//唤醒
        }finally {
            lock.unlock();//释放锁
        }
    }

    
    public void decrementProduct() throws InterruptedException {
        lock.lock();
        try {
            //1判断商店是否有商品
            while (num==0){
                this.wait();
//                condition.await();//等待
            }
            //2消费商品
            System.out.println(Thread.currentThread().getName()+"号顾客购买商品,商品号为"+num);
            num--;
            //3通知生产者
            this.notifyAll();
//            condition.signalAll();//唤醒
        }finally {
            lock.unlock();//释放锁
        }
    }
}

public class ProductTest {
    public static void main(String[] args) {
        ProConShop shop = new ProConShop();
        for (int i = 1; i <= 5; i++) {
            new Thread(()->{
                try {
                    shop.incrementProduct();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            },"店员1").start();
        }
        for (int i = 1; i <= 5; i++) {
            new Thread(()->{
                try {
                    shop.decrementProduct();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            },"第一批顾客第"+i).start();
        }
    }
}

运行代码报错

解决方式用 Condition的等待await()和唤醒signalAll()代替之前的wait()和notifyAll()

Condition 将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 set(wait-set)。其中,Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用。

条件(也称为条件队列 或条件变量)为线程提供了一个含义,以便在某个状态条件现在可能为 true 的另一个线程通知它之前,一直挂起该线程(即让其“等待”)。因为访问此共享状态信息发生在不同的线程中,所以它必须受保护,因此要将某种形式的锁与该条件相关联。等待提供一个条件的主要属性是:以原子方式 释放相关的锁,并挂起当前线程,就像 Object.wait 做的那样。

Condition 实例实质上被绑定到一个锁上。要为特定 Lock 实例获得 Condition 实例,请使用其 newCondition() 方法。

2.2精准通知顺序访问

采用lock.Condition还有一个好处就是可以精准的唤醒你想执行的线程

例如:①多线程之间按顺序调用,实现A->B->C②红绿灯场景

红绿灯场景模拟代码

package com.nie.juc;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

class TrafficLight{
    Lock lock = new ReentrantLock();
    Condition green = lock.newCondition();//绿灯
    Condition yellow = lock.newCondition();//黄灯
    Condition red = lock.newCondition();//红灯
    //1-green  2-yellow 3-red
   volatile int state = 1;

    public void greenLight(){
        lock.lock();
        try {
            if (state!=1){ //判断是否为绿灯,不是的话等待
                green.await();
            } else {
                System.out.println(Thread.currentThread().getName()+"t 绿灯行=========");
                TimeUnit.SECONDS.sleep(1);
                //通知黄灯
                state=2;
                yellow.signal();
//                green.signalAll(); //行不通
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void yellowLight(){
        lock.lock();
        try {
            if (state!=2){ //判断是否为黄灯,不是的话等待
                yellow.await();
            }else {
                System.out.println(Thread.currentThread().getName()+"t 黄灯等一等=========");
                TimeUnit.SECONDS.sleep(1);
                //通知红灯
                state=3;
                red.signal();
//                yellow.signalAll();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void redLight(){
        lock.lock();
        try {
            if (state!=3){ //判断是否为红灯,不是的话等待
                red.await();
            }else {
                System.out.println(Thread.currentThread().getName()+"t 红灯停=========");
                TimeUnit.SECONDS.sleep(1);
                //通知绿灯
                state=1;
                green.signal();
//                red.signalAll();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

public class TrafficLightDemo {
    public static void main(String[] args) {
        TrafficLight trafficLight = new TrafficLight();
        new Thread(()->{
            while (true) {
                trafficLight.greenLight();
            }
        }).start();
        new Thread(()->{
            while (true) {
                trafficLight.yellowLight();
            }
        }).start();
        new Thread(()->{
            while (true) {
                trafficLight.redLight();
            }
        }).start();
    }
}

打印结果为:

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/4828566.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-11-10
下一篇 2022-11-10

发表评论

登录后才能评论

评论列表(0条)

保存