Lock版的生产者消费者问题

简介: Lock版的生产者消费者问题

线程之间的通信问题 生产者和消费者问题

生产者和消费者代码编写思路:判断是否等待 进行业务处理 通知其他线程

    判断是否需要等待,判断完之后就干活,如果需要等待就等待,干完活就通知其他线

  新版(JUC)生产者和消费者问题

  回顾:传统的synchronized解决生产者和消费者问题有wait(等待)和notify(唤醒)方法,那么使用JUC实现生产者和消

通过lock找到condition(它是一个同步监视器),使用condition的await()方法和signalAll(唤醒全部)方法


//创建Lock锁实现类可重入锁
Lock lock = new ReentrantLock();
//通过可重入锁创建condition对象 它是一个同步监视器
Condition condition = lock.newCondition();
//等待     相当于synchronized的wait() 也需要抛异常
condition.await();
//唤醒全部 相当于synchronized的nofityAll()
condition.signalAll();

synchronized:wait(等待) notify(唤醒)

lock-->contidition:await(等待) signaAlll(唤醒全部)

代码实现


package com.wyh.pc;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
 * @program: JUC
 * @description: 使用JUC下的lock下的condition的await和signal实现消费者生产者线程通信
 * @author: 魏一鹤
 * @createDate: 2022-02-12 22:34
 **/
//使用JUC下的lock下的condition的await和signal实现消费者生产者线程通信
public class B {
public static void main(String[] args){
//线程操作资源类
        //创建资源类
        Data2 data=new Data2();
//多线程处理 把资源抛给线程去执行 这里采用lambda表达式简化代码 ,第二个参数是现成名称
        new Thread(()->{
//从10循环+1
            //由于方法里面wait()方法需要抛异常,这里调用也需要抛异常
            for (int i = 0; i < 10; i++) {
try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"线程A").start();
new Thread(()->{
//从10循环-1
            //由于方法里面wait()方法需要抛异常,这里调用也需要抛异常
            for (int i = 0; i < 10; i++) {
try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"线程B").start();
new Thread(()->{
//从10循环-1
            //由于方法里面wait()方法需要抛异常,这里调用也需要抛异常
            for (int i = 0; i < 10; i++) {
try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"线程C").start();
new Thread(()->{
//从10循环-1
            //由于方法里面wait()方法需要抛异常,这里调用也需要抛异常
            for (int i = 0; i < 10; i++) {
try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"线程D").start();
    }
}
//静态资源  独立耦合的 必须降低耦合性
class Data2{
// 属性 数字变量
    private  int num=0;
 //创建Lock锁实现类可重入锁
    Lock lock = new ReentrantLock();
    //通过可重入锁创建condition对象 它是一个同步监视器
    Condition condition = lock.newCondition();
//方法 数字+1 自增1
    public  void increment() throws InterruptedException {
        //IDEA中try catch快捷键 选中代码ctrl+alt+t 活在在菜单栏选择code选中try catch自动生成代码块
        //lock需要和try catch finally代码块配合使用 try中加锁(lock)并且写入业务代码,finally中解锁(unLock)
        try {
//加锁
            lock.lock();
//为了防止虚假唤醒,应该用while进行判断等待
            while (num!=0){
 //等待 相当于synchronized的wait()  和wait一样也需要抛异常
                condition.await();
            }
num++;
            System.out.println(Thread.currentThread().getName() + "-->"+num);
//操作完之后通知其他线程,我+1完毕了
            //唤醒全部 相当于synchronized的notifyAll()
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
//解锁
            lock.unlock();
        }
    }
//方法 数字-1 自减1
    public  void decrement() throws InterruptedException {
        //IDEA中try catch快捷键 选中代码ctrl+alt+t 活在在菜单栏选择code选中try catch自动生成代码块
        //lock需要和try catch finally代码块配合使用 try中加锁(lock)并且写入业务代码,finally中解锁(unLock)
        try {
 //加锁
            lock.lock();
 //为了防止虚假唤醒,应该用while进行判断等待
            while(num==0){
//等待 相当于synchronized的wait()  和wait一样也需要抛异常  和wait一样也需要抛异常
                condition.await();
            }
num--;
            System.out.println(Thread.currentThread().getName() + "-->"+num);
//操作完之后通知其他线程,我-1完毕了
            //唤醒全部 相当于synchronized的notifyAll()
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
 //解锁
            lock.unlock();
        }
    }
}


观察控制台发现效果和上面传统的synchronized效果是一样的,不同之处是使用的对象以及方法不同


image.png


线程A-->1

线程B-->0

线程C-->1

线程B-->0

线程C-->1

线程B-->0

线程C-->1

线程B-->0

线程C-->1

线程B-->0

线程C-->1

线程B-->0

线程C-->1

线程B-->0

线程C-->1

线程B-->0

线程C-->1

线程B-->0

线程C-->1

线程B-->0

线程C-->1

线程D-->0

线程A-->1

线程D-->0

线程A-->1

线程D-->0

线程A-->1

线程D-->0

线程A-->1

线程D-->0

线程A-->1

线程D-->0

线程A-->1

线程D-->0

线程A-->1

线程D-->0

线程A-->1

线程D-->0

线程A-->1

线程D-->0


通过观察结果,发现线程执行并没有安装我们想要的ABCD四个线程有序轮流执行,处于随机分布的执行状态,和synchronized执行顺序一样,那么既然这样,为什么不使用传统的synchronized而原则condition(lock)呢?

任何一个新技术,绝对不是仅仅只是覆盖了原来的老技术,肯定在老技术的基础上新增了优势和补充

condition精准的通知和唤醒线程:上面线程执行是随机分配的,不是有序执行的,如果想按照有序执行,那么应该是A->B->C->D这样的执行顺序,那么condition就可以帮我们实现这点,这也是它比传统synchronized的优势

目录
相关文章
|
5月前
|
监控 安全 Java
Java中的锁(Lock、重入锁、读写锁、队列同步器、Condition)
Java中的锁(Lock、重入锁、读写锁、队列同步器、Condition)
29 0
|
Java
JUC基础(三)—— Lock锁 及 AQS(2)
JUC基础(三)—— Lock锁 及 AQS
94 0
|
算法 调度
JUC基础(三)—— Lock锁 及 AQS(1)
JUC基础(三)—— Lock锁 及 AQS
129 0
高并发编程-使用wait和notifyAll进行线程间的通信3_多线程下的生产者消费者模型和notifyAll
高并发编程-使用wait和notifyAll进行线程间的通信3_多线程下的生产者消费者模型和notifyAll
69 0
【JUC基础】04. Lock锁
java.util.concurrent.locks为锁定和等待条件提供一个框架的接口和类,说白了就是锁所在的包。
5519 0
JUC学习(三):synchronized和Lock实现线程间通信(包含虚假唤醒的讲解)
JUC学习(三):synchronized和Lock实现线程间通信(包含虚假唤醒的讲解)
JUC学习(三):synchronized和Lock实现线程间通信(包含虚假唤醒的讲解)
|
安全 Java
多线程详解p18、Lock锁
多线程详解p18、Lock锁
|
调度
【多线程:锁】生产者消费者
【多线程:锁】生产者消费者
176 0