JUC学习(二)- 线程间的通信

一、线程间的通信

(一)多线程交互的虚假唤醒

(1)虚假唤醒重现
//资源类
class AirConditioner{
    private int number = 0;
    
    public synchronized void increment() throws InterruptedException {
        //1判断
        if(number != 0) {
            this.wait();
        }
        //2干活
        number++;
        System.out.println(Thread.currentThread().getName() + "\t" + number);
        //3通知
        this.notifyAll();
    }

    public synchronized void decrement() throws InterruptedException {
        //1判断
        if(number == 0) {
            this.wait();
        }
        //2干活
        number--;
        System.out.println(Thread.currentThread().getName() + "\t" + number);
        //3通知
        this.notifyAll();
    }
}

// 线程操作资源类
public class ThreadWaitNotifyDemo {

    public static void main(String[] args) {
        AirConditioner airConditioner =  new AirConditioner();

        new Thread(()->{
            for (int i = 1; i <=10 ; i++) {
                try {
                    airConditioner.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A").start();

        new Thread(()->{
            for (int i = 1; i <=10 ; i++) {
                try {
                    airConditioner.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();

        new Thread(()->{
            for (int i = 1; i <=10 ; i++) {
                try {
                    airConditioner.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"C").start();

        new Thread(()->{
            for (int i = 1; i <=10 ; i++) {
                try {
                    airConditioner.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"D").start();
    }
}

当有两个生产者,两个消费者操作资源类,就会发生问题
JUC学习(二)- 线程间的通信

原因

  • A线程(生产者)获取资源类,发现number = 0,执行number++,并唤醒所有线程
  • C线程(生产者)获取资源类,发现number =1,执行等待
  • A继续获取操作权,发现number = 1 ,等待
  • B线程获取资源类,number–,唤醒其他等待线程
  • 此时A线程被唤醒,从刚才阻塞的位置继续进行,不必再if()判断了,直接number++,并唤醒其他等待线程
  • C线程拿到操作权,if()中的wait()执行完毕,继续执行number++,此时出现number = 3;
(2)解决

将资源类的if()判断更换成while()循环判断即可,执行完wait()继续判断是否满足条件,只有满足条件才继续执行

(二)线程通信的两种写法

  1. synchronized()中使用wait()notifyAll()
  2. lock()中使用Condition()await()signalAll()

两种方法的区别(为什么要用lock()替换synchronized()):
lock()Condition()能够精准通知,精准唤醒

案例

多线程之间按顺序调用,实现A->B->C
三个线程启动,要求如下:
AA打印5次,BB打印10次,CC打印15次
接着
AA打印5次,BB打印10次,CC打印15次

// 资源类
class ShareResource{
    private int number = 1;  // 1:A  2:B 3:C
    private Lock lock = new ReentrantLock();
    private Condition condition1 = lock.newCondition();
    private Condition condition2 = lock.newCondition();
    private Condition condition3 = lock.newCondition();

    /**
     * AA线程打印5次
     */
    public void print5(){
        lock.lock();
        try{
            // 1.判断
            while (number !=1 ){
                condition1.await();
            }
            // 2.干活
            for (int i = 1; i <= 5; i++) {
                System.out.println(Thread.currentThread().getName()+"\t"+i);
            }
            // 3.通知
            number = 2;
            condition2.signal();  //此处使print10()方法中的condition2.await()的线程被唤醒
        }catch(Exception e){
        }finally{
        lock.unlock();
        }
    }

    /**
     * BB线程打印10次
     */
    public void print10(){
        lock.lock();
        try{
            // 1.判断
            while (number!=2){
                condition2.await();
            }
            // 2.干活
            for (int i = 1; i <= 10; i++) {
                System.out.println(Thread.currentThread().getName()+"\t"+i);
            }
            // 3.通知
            number = 3;
            condition3.signal();
        }catch(Exception e){
        }finally{
        lock.unlock();
        }
    }

    /**
     * CC线程打印15次
     */
    public void print15() {

        lock.lock();
        try {
            // 1.判断
            while (number != 3) {
                condition3.await();
            }
            // 2.干活
            for (int i = 1; i <= 15; i++) {
                System.out.println(Thread.currentThread().getName()+"\t"+i);
            }
            // 3.唤醒
            number = 1;
            condition1.signal();
        } catch (Exception e) {
        } finally {
            lock.unlock();
        }
    }
}

public class ThreadOrderAccess {

    public static void main(String[] args) {
        ShareResource shareResource = new ShareResource();
        new Thread(() -> {
            for (int i = 1; i <= 10; i++) {
                shareResource.print5();
            }
        }, "A").start();

        new Thread(() -> {
            for (int i = 1; i <= 10; i++) {
                shareResource.print10();
            }
        }, "B").start();

        new Thread(() -> {
            for (int i = 1; i <= 10; i++) {
                shareResource.print15();
            }
        }, "C").start();
    }
}

上一篇:JUC常用类笔记


下一篇:Java——JUC高并发编程,面试必问( 集合的线程安全)