生产者与消费者的两种方式

一、普通版

synchronized版,使用wait、notify

public class Demo01 {
    public static void main(String[] args) {
        Shop shop = new Shop();
        //生产者去提供商品
        new Thread(()->{

            for (int i = 0; i < 10; i++) {
                try {
                    shop.buyGoods(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        },"生产者").start();
        
        //消费者A去购买商品
        new Thread(()->{
            for (int i = 0; i < 50; i++) {
                try {
                    shop.saleGoods(5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"消费者A").start();

        //消费者B去购买商品
        new Thread(()->{
            for (int i = 0; i < 50; i++) {
                try {
                    shop.saleGoods(5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"消费者B").start()
    }
}

class Shop{

    private int goodsNum = 50;
    //
    public synchronized void saleGoods(int num) throws InterruptedException {

        while (goodsNum==0){
            this.wait();
        }

        goodsNum -= num;
        System.out.println(Thread.currentThread().getName()+"买走了"+num+"件商品,还剩余"+goodsNum+"件商品");
        this.notifyAll();
    }

    public synchronized void buyGoods(int num) throws InterruptedException {

        while (goodsNum!=0){
            this.wait();
        }
        goodsNum += num;
        System.out.println(Thread.currentThread().getName()+"送来了"+num+"件商品,还剩余"+goodsNum+"件商品");
        this.notifyAll();
    }
}

有可能出现的问题:虚假唤醒

表现:

  虚假唤醒的表现为: 有时候资源会出现"超卖"现象. 也就是出现负数, 而这是不应该出现的. 关键点就是判断合法性的时候, 使用了if进行判断.

过程:

- 在消费者进入到购买方法中时,会告知商品为0,发生阻塞(wait)会将锁释放,这是其他消费线程也进入,发生阻塞,等到生产者将商品补充之后,再去进行商品的购买(num--),这时if只会判断一次,所以就可能发生num=-1;

解决办法:

   将if换成while

二、高级版

使用Lock锁和 Condition

获取Condition的方法:

Lock lock = new ReentrantLock();

Condition condition1 = lock.newCondition();

Condition主要方法:

  • await():等待

  • signal():唤醒一个等待线程

  • signalAll():唤醒全部等待线程

public class Test01 {
    public static void main(String[] args) {
        A a = new A();
        new Thread(()->{for (int i = 0; i < 10; i++) {a.increment();}
        },"线程A").start();
        new Thread(()->{for (int i = 0; i < 10; i++) {a.decrement();}
        },"线程B").start();
        new Thread(()->{ for (int i = 0; i < 10; i++) { a.increment();}
        },"线程C").start();
        new Thread(()->{  for (int i = 0; i < 10; i++) {a.decrement(); }
        },"线程D").start();

    }
}
class A {

    private int num = 0;
//获得锁
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();//获取Condition对象

    public void increment()  {
        try {
            lock.lock();
            while (num!=0){
                condition.await();//等待
            }
            num++;
            System.out.println(Thread.currentThread().getName()+"->"+num);
            condition.signalAll();//唤醒
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }

    }
    public synchronized void decrement()  {
        try {
            lock.lock();
            while (num==0){
                condition.await();
            }
            num--;
            System.out.println(Thread.currentThread().getName()+"->"+num);
            condition.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
}

使用Condition精准通知和唤醒线程:

//有三个方法:
public void printA(){}//线程A执行
public void printB(){}//线程B执行
public void printC(){}//线程C执行

private Condition condition1 = lock.newCondition();//监听线程A
private Condition condition2 = lock.newCondition();//监听线程B
private Condition condition3 = lock.newCondition();//监听线程C

//在printA方法执行完之后,用Condition2去唤醒线程B执行priintB
//在printB方法执行完之后,用Condition3去唤醒线程B执行priintC
//在printA方法执行完之后,用Condition1去唤醒线程B执行priintA
//可以达到Condition精准的通知和唤醒线程

具体操作代码:

/**
 * A执行完调用B,B执行完调用C C执行完调用A
 */
public class Test02 {
    public static void main(String[] args) {
        Data1 data1 = new Data1();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                data1.printA();
            }
        },"A").start();
        //线程B
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                data1.printB();
            }
        },"B").start();
        //线程C
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                data1.printC();
            }
        },"C").start();
    }
}

class Data1{
    private Lock lock = new ReentrantLock();
    private Condition condition1 = lock.newCondition();//监听线程A
    private Condition condition2 = lock.newCondition();//监听线程B
    private Condition condition3 = lock.newCondition();//监听线程C

    private int num = 1;
    public void printA(){
        try {
            lock.lock();
            // 业务,判断-> 执行-> 通知
            while (num!=1){
                condition1.await();
            }
            num=2;
            //通知condition2去唤醒线程B
            condition2.signal();
            System.out.println(Thread.currentThread().getName()+":->AAAA");


        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }


    }
    public void printB(){
        try {
            lock.lock();
            while (num!=2){
                condition2.await();
            }
            num = 3;
            //通知condition3去唤醒线程C
            condition3.signal();
            System.out.println(Thread.currentThread().getName()+":->BBBB");
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }

    }
    public void printC(){
        try {
            lock.lock();
            while (num!=3){
                condition3.await();
            }
            num = 1;
            //通知condition1去唤醒线程A
            condition1.signal();
            System.out.println(Thread.currentThread().getName()+":->CCCC");
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }

    }

}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值