lock(condition)实现精准通知唤醒线程和Lock版的生产者消费者问题

简介: lock(condition)实现精准通知唤醒线程

上代码


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: Condition实现精准通知唤醒
 * @author: 魏一鹤
 * @createDate: 2022-02-13 19:20
 **/
//利用Condition实现精准通知唤醒
//我们想要的执行顺序,A执行完执行B B执行完执行C C执行完执行A
public class C {
public static void main(String[] args){
//静态资源类
        Data3 data3 = new Data3();
        //多个线程操作同一个静态资源类(并发) 简化语法使用lambda表达式 10次for循环执行方法
        //如果for循环里面只有一行代码 可以省去大括号"{}"
        new Thread(()->{ for (int i = 0; i < 10; i++) data3.printA(); },"线程A").start();
new Thread(()->{ for (int i = 0; i < 10; i++) data3.printB(); },"线程B").start();
new Thread(()->{ for (int i = 0; i < 10; i++) data3.printC(); },"线程C").start();
    }
}
//资源类
class Data3{
//lock可重入锁
    Lock lock=new ReentrantLock();
    //使用lock创建一个同步监视器 创建三个,监视不同的对象  利用Condition实现精准通知
    Condition conditionA = lock.newCondition();
    Condition conditionB = lock.newCondition();
    Condition conditionC = lock.newCondition();
    //变量标识 如果等于1就是线程A执行  如果等于2就是线程B执行 如果等于3就是线程C执行
    private int num=1;
//A线程打印方法
    public void printA(){
try {
//加锁 业务代码也写在try中
            lock.lock();
//通知者消费者写代码思路 :判断等待 执行代码  通知其他线程
            //为了防止虚拟唤醒 使用while循环判断
            while (num!=1) {
//等待
                conditionA.await();
            }
            System.out.println(Thread.currentThread().getName() + "AAAAAAA");
//num=2 线程B执行
            num=2;
          //精准唤醒conditionB 不使用signalAll方法唤醒全部 而是调用某一个具体的同步监视器唤醒具体的
            conditionB.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
//解锁
            lock.unlock();
        }
    }
//B线程打印方法
    public void printB(){
try {
//加锁 业务代码也写在try中
            //通知者消费者写代码思路 :判断等待 执行代码  通知其他线程
            lock.lock();
//为了防止虚拟唤醒 使用while循环判断
            while (num != 2) {
//唤醒
                conditionB.await();
            }
            System.out.println(Thread.currentThread().getName() + "BBBBBBB");
//num=3 线程C执行
            num=3;
//精准唤醒conditionC
            conditionC.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
//解锁
            lock.unlock();
        }
    }
//C线程打印方法
    public void printC(){
try {
//加锁 业务代码也写在try中
            //通知者消费者写代码思路 :判断等待 执行代码  通知其他线程
            lock.lock();
//为了防止虚拟唤醒 使用while循环判断
            while (num != 3) {
//唤醒
                conditionC.await();
            }
            System.out.println(Thread.currentThread().getName() + "CCCCCC");
//num=1 线程A执行
            num=1;
            //精准唤醒conditionA
            conditionA.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
//解锁
            lock.unlock();
        }
    }
}


Lock版的生产者消费者问题




线程AAAAAAAA

线程BBBBBBBB

线程CCCCCCC

线程AAAAAAAA

线程BBBBBBBB

线程CCCCCCC

线程AAAAAAAA

线程BBBBBBBB

线程CCCCCCC

线程AAAAAAAA

线程BBBBBBBB

线程CCCCCCC

线程AAAAAAAA

线程BBBBBBBB

线程CCCCCCC

线程AAAAAAAA

线程BBBBBBBB

线程CCCCCCC

线程AAAAAAAA

线程BBBBBBBB

线程CCCCCCC

线程AAAAAAAA

线程BBBBBBBB

线程CCCCCCC

线程AAAAAAAA

线程BBBBBBBB

线程CCCCCCC

线程AAAAAAAA

线程BBBBBBBB

线程CCCCCCC



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

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

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

  新版(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


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

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

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

目录
相关文章
|
2月前
|
安全 编译器 C#
C#学习相关系列之多线程---lock线程锁的用法
C#学习相关系列之多线程---lock线程锁的用法
|
4月前
|
数据处理
多线程与并发编程【线程对象锁、死锁及解决方案、线程并发协作、生产者与消费者模式】(四)-全面详解(学习总结---从入门到深化)
多线程与并发编程【线程对象锁、死锁及解决方案、线程并发协作、生产者与消费者模式】(四)-全面详解(学习总结---从入门到深化)
44 1
|
2天前
|
监控 安全 Java
【多线程学习】深入探究阻塞队列与生产者消费者模型和线程池常见面试题
【多线程学习】深入探究阻塞队列与生产者消费者模型和线程池常见面试题
|
3月前
|
消息中间件 安全 Java
多线程(初阶七:阻塞队列和生产者消费者模型)
多线程(初阶七:阻塞队列和生产者消费者模型)
31 0
|
4月前
|
Java C++
线程池-手写线程池C++11版本(生产者-消费者模型)
线程池-手写线程池C++11版本(生产者-消费者模型)
74 0
|
4月前
|
Java Linux C语言
线程池-手写线程池Linux C简单版本(生产者-消费者模型)
线程池-手写线程池Linux C简单版本(生产者-消费者模型)
44 0
|
4月前
|
安全 Java
java多线程之Lock锁原理以及案例实现电影院卖票
java多线程之Lock锁原理以及案例实现电影院卖票
|
4月前
|
Java
Java多线程同步锁、Lock锁和等待唤醒机制及代码演示
Java多线程同步锁、Lock锁和等待唤醒机制及代码演示
|
5月前
|
SQL 供应链 安全
Linux多线程【生产者消费者模型】
Linux多线程【生产者消费者模型】
59 0
|
5月前
|
Java
多线程并发之显示锁Lock与其通信方式Condition源码解读
多线程并发之显示锁Lock与其通信方式Condition源码解读
23 0