加载中...

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:它可以精准的通知和唤醒线程;

 
posted @ 2020-04-05 17:24  luckyjcx  阅读(526)  评论(0编辑  收藏  举报