生产者与消费者问题?

生产者消费者模式是并发、多线程编程中经典的设计模式。

        简单来看,就是一个类负责生产,一个类负责消费。举例来说,一个变量,生产者不断增加这个变量,消费者不断减少这个变量。在互联网应用中,抢票机制就是应用了该模式,比如大麦网演唱会门票抢票,12306火车票抢票等。

1、生产者与消费者问题 synchronized 版本

 假设有两个线程A和B,操作同一个变量,A线程+1,B线程-1, 交替循环进行

package cn.dczh.juc;

/**
 * @ClassName: A
 * @description: A线程 B线程 操作同一个变量 num= 0  A:+1   B:-1
 * @author: 
 * @date: 2022/3/16 21:34
 * @version: 1.0
 */
public class A {
    public static void main(String[] args) {
        Data data = new Data();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.incr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "A").start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.decr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "B").start();
    }
}

class Data {

    private int num = 0;

    public synchronized void incr() throws InterruptedException {
        if (num != 0) {
            //等待
            this.wait();
        }
        //加一
        num++;
        System.out.println(Thread.currentThread().getName() + "线程加一:" + num);
        //通知其他线程 我加一完毕了
        this.notifyAll();
    }

    public synchronized void decr() throws InterruptedException {
        if (num == 0) {
            //等待
            this.wait();
        }
        num--;
        System.out.println(Thread.currentThread().getName() + "线程减一:" + num);
        //通知其他线程 我减一完毕了
        this.notifyAll();
    }
}

2、如果有四个线程A、B、C、D同时执行呢?(虚假唤醒问题?)

package cn.dczh.juc;

/**
 * @ClassName: A
 * @description: A线程 B线程 操作同一个变量 num= 0  A:+1   B:-1
 * @author: 
 * @date: 2022/3/16 21:34
 * @version: 1.0
 */
public class A {
    public static void main(String[] args) {
        Data data = new Data();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.incr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "A").start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.decr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "B").start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.incr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "C").start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.decr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "D").start();
    }
}

class Data {

    private int num = 0;

    public synchronized void incr() throws InterruptedException {
        if (num != 0) {
            //等待
            this.wait();
        }
        //加一
        num++;
        System.out.println(Thread.currentThread().getName() + "线程加一:" + num);
        //通知其他线程 我加一完毕了
        this.notifyAll();
    }

    public synchronized void decr() throws InterruptedException {
        if (num == 0) {
            //等待
            this.wait();
        }
        num--;
        System.out.println(Thread.currentThread().getName() + "线程减一:" + num);
        //通知其他线程 我减一完毕了
        this.notifyAll();
    }
}

该代码执行结果为:

这种现象称为虚假唤醒 ! 如何解决这种问题呢?  

将代码中的if判断改文文档中的while判断即可

class Data {

    private int num = 0;

    public synchronized void incr() throws InterruptedException {
        while (num != 0) {
            //等待
            this.wait();
        }
        //加一
        num++;
        System.out.println(Thread.currentThread().getName() + "线程加一:" + num);
        //通知其他线程 我加一完毕了
        this.notifyAll();
    }

    public synchronized void decr() throws InterruptedException {
        while (num == 0) {
            //等待
            this.wait();
        }
        num--;
        System.out.println(Thread.currentThread().getName() + "线程减一:" + num);
        //通知其他线程 我减一完毕了
        this.notifyAll();
    }
}

执行结果为:

 3、生产者与消费者问题 JUC版本

 

package cn.dczh.juc;

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

/**
 * @ClassName: B
 * @description:
 * @author: 
 * @date: 2022/3/16 22:08
 * @version: 1.0
 */
public class B {
    public static void main(String[] args) {
        Data2 data = new Data2();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) data.incr();
        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) data.decr();
        }, "B").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) data.incr();
        }, "C").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) data.decr();
        }, "D").start();

    }
}

class Data2 {

    private int num = 0;
    //获取lock锁
    final Lock lock = new ReentrantLock();
    //通过lock锁获取对象监视器
    final Condition condition = lock.newCondition();

    public void incr() {
        lock.lock();
        try {
            while (num != 0) {
                //等待
                condition.await();
            }
            //加一
            num++;
            System.out.println(Thread.currentThread().getName() + "线程加一:" + num);
            //通知其他线程 我加一完毕了
            condition.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }

    public void decr() {
        lock.lock();
        try {
            while (num == 0) {
                //等待
                condition.await();
            }
            num--;
            System.out.println(Thread.currentThread().getName() + "线程减一:" + num);
            //通知其他线程 我减一完毕了
            condition.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }
}


4、Condition实现精种通知、唤醒线程 

 现有四个方法printA、printB、printC、printD,由四个线程A、B、C、D执行,A线程执行printA方法,执行完毕后唤醒B线程,执行printB方法,执行完毕后唤醒C线程执行printC方法,执行完毕后唤醒D线程,执行pringD方法,执行完毕后唤醒A线程 依此循环执行;

package cn.dczh.juc;

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

/**
 * @ClassName: C
 * @description:
 * @author: 
 * @date: 2022/3/16 22:22
 * @version: 1.0
 */
public class C {
    public static void main(String[] args) {
        Data3 data = new Data3();
        new Thread(() -> { for (int i = 0; i < 10; i++) data.printA(); }, "A").start();
        new Thread(() -> { for (int i = 0; i < 10; i++) data.printB(); }, "B").start();
        new Thread(() -> { for (int i = 0; i < 10; i++) data.printC(); }, "C").start();
        new Thread(() -> { for (int i = 0; i < 10; i++) data.printD(); }, "D").start();

    }
}

class Data3 {

    //获取lock锁
    final Lock lock = new ReentrantLock();

    final Condition conditionA = lock.newCondition();
    final Condition conditionB = lock.newCondition();
    final Condition conditionC = lock.newCondition();
    final Condition conditionD = lock.newCondition();

    //1:A线程 2:B线程 3:C线程 4:D线程
    private int flag = 1;

    public void printA(){
        try {
            lock.lock();
            while (flag != 1){
                //等待
                conditionA.await();
            }
            System.out.println("线程"+Thread.currentThread().getName()+"执行完毕!");
            //修改标示为2
            flag = 2;
            //唤醒指定的线程 B
            conditionB.signal();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
    public void printB(){
        try {
            lock.lock();
            while (flag != 2){
                //等待
                conditionB.await();
            }
            System.out.println("线程"+Thread.currentThread().getName()+"执行完毕!");
            //修改标示为2
            flag = 3;
            //唤醒指定的线程 C
            conditionC.signal();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
    public void printC(){
        try {
            lock.lock();
            while (flag != 3){
                //等待
                conditionC.await();
            }
            System.out.println("线程"+Thread.currentThread().getName()+"执行完毕!");
            //修改标示为2
            flag = 4;
            //唤醒指定的线程 D
            conditionD.signal();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
    public void printD(){
        try {
            lock.lock();
            while (flag != 4){
                //等待
                conditionD.await();
            }
            System.out.println("线程"+Thread.currentThread().getName()+"执行完毕!");
            //修改标示为2
            flag = 1;
            //唤醒指定的线程 A
            conditionA.signal();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }


}

执行结果如下:

小结 

 锁:锁的是new this 锁的是对象,static Clazz 锁的是唯一的类模板;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值