线程之间的通信问题 生产者和消费者问题
生产者和消费者代码编写思路:判断是否等待 进行业务处理 通知其他线程
判断是否需要等待,判断完之后就干活,如果需要等待就等待,干完活就通知其他线
新版(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效果是一样的,不同之处是使用的对象以及方法不同
线程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的优势