Java并发编程虚假唤醒问题(生产者和消费者关系)
何为虚假唤醒:
当一个条件满足时,很多线程都被唤醒了,但是只有其中部分是有用的唤醒,其它的唤醒都是无用功;
比如买货:如果商品本来没有货物,突然进了一件商品,这是所有的线程都被唤醒了,但是只能一个人买,所以其他人都是假唤醒,获取不到对象的锁;
避免虚假唤醒:
Synchronized版,生产者和消费者问题
package com.jia.pc; public class A { public static void main(String[] args) { Data data = new Data(); new Thread(()->{ for (int i = 0; i < 10 ; i++) { try { data.increment(); } catch (InterruptedException e) { e.printStackTrace(); } } },"A").start(); new Thread(()->{ for (int i = 0; i < 10 ; i++) { try { data.decrement(); } catch (InterruptedException e) { e.printStackTrace(); } } },"B").start(); new Thread(()->{ for (int i = 0; i < 10 ; i++) { try { data.increment(); } catch (InterruptedException e) { e.printStackTrace(); } } },"C").start(); new Thread(()->{ for (int i = 0; i < 10 ; i++) { try { data.decrement(); } catch (InterruptedException e) { e.printStackTrace(); } } },"D").start(); } } // 等待,业务,通知 class Data{ private int number = 0; // +1 public synchronized void increment() throws InterruptedException { while (number != 0){ // 等待 this.wait(); } number++; System.out.println(Thread.currentThread().getName() + "->" + number); // 通知其他线程,我+1完毕 this.notifyAll(); } // -1 public synchronized void decrement() throws InterruptedException { while (number == 0){ // 等待 this.wait(); } number--; System.out.println(Thread.currentThread().getName() + "->" + number); // 通知其他线程,我-1完毕 this.notifyAll(); } }
运行结果:
A->1 B->0 A->1 B->0 A->1 B->0 A->1 B->0 A->1 B->0 A->1 B->0 A->1 B->0 A->1 B->0 A->1 B->0 A->1 B->0 C->1 D->0 C->1 D->0 C->1 D->0 C->1 D->0 C->1 D->0 C->1 D->0 C->1 D->0 C->1 D->0 C->1 D->0 C->1 D->0 Process finished with exit code 0
虚假幻想是如何产生的?
把 while (number != 0) {}
换成 if (number == 0) {}
就会出现虚假唤醒。官方文档有标注;
为什么if判断会出现虚假唤醒?
1. 因为if只会执行一次,执行完会接着向下执行if()外边的
2. 而while不会,直到条件满足才会向下执行while()外边的
JUC版,生产者和消费者问题
使用 Condition 代码实现:
package com.jia.pc; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; 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(); } } // 资源类 class Data3{ private Lock lock = new ReentrantLock(); Condition conditionA = lock.newCondition(); Condition conditionB = lock.newCondition(); Condition conditionC = lock.newCondition(); private int number = 1; public void printA(){ lock.lock(); try { while (number != 1){ //等待 conditionA.await(); } System.out.println(Thread.currentThread().getName()+"->"+"AAAAA"); //唤醒执行的线程 B number = 2; conditionB.signal(); } catch (Exception e) { e.printStackTrace(); }finally { lock.unlock(); } } public void printB(){ lock.lock(); try { while (number != 2){ //等待 conditionB.await(); } System.out.println(Thread.currentThread().getName()+"->"+"BBBBB"); //唤醒 C number = 3; conditionC.signal(); } catch (Exception e) { e.printStackTrace(); }finally { lock.unlock(); } } public void printC(){ lock.lock(); try { while (number != 3){ //等待 conditionC.await(); } System.out.println(Thread.currentThread().getName()+"->"+"CCCCC"); //唤醒 A number = 1; conditionA.signal(); } catch (Exception e) { e.printStackTrace(); }finally { lock.unlock(); } } }
Condition:它可以精准的通知和唤醒线程;