上代码
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效果是一样的,不同之处是使用的对象以及方法不同
通过观察结果,发现线程执行并没有安装我们想要的ABCD四个线程有序轮流执行,处于随机分布的执行状态,和synchronized执行顺序一样,那么既然这样,为什么不使用传统的synchronized而原则condition(lock)呢?
任何一个新技术,绝对不是仅仅只是覆盖了原来的老技术,肯定在老技术的基础上新增了优势和补充
condition精准的通知和唤醒线程:上面线程执行是随机分配的,不是有序执行的,如果想按照有序执行,那么应该是A->B->C->D这样的执行顺序,那么condition就可以帮我们实现这点,这也是它比传统synchronized的优势