JUC学习笔记|线程间通信

线程间通信

生成者消费者模型

/**
 * @author phd
 * @version 1.0
 * @date 2020/8/31 22:36
 * 生产者消费者模型练习
 */

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

    public synchronized void decreament() throws InterruptedException {
        if(number == 0){ //存在虚假唤醒 if 改成 while
            this.wait();
        }
        number-- ;
        System.out.println(Thread.currentThread().getName()+"\t"+number);
        this.notifyAll();

    }
}



public class ThreadWaitNotifyDemo {
    
    public static void main(String args[]) {
        AirConditioner airConditioner = new AirConditioner();
        new Thread(()->{
            try {
                for (int i = 0; i < 10; i++) {
                    airConditioner.increment();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"A").start();

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

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

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

}

虚假唤醒

以上代码,换成4个线程会导致错误。
原因:在java多线程判断时,不能用if,程序出事出在了判断上面,突然有一个添加的线程进到了if,突然中断了交出控制权,没有进行验证,而是直接走下去了,加了两次,甚至多次。

解决办法

解决虚假唤醒:查看API,java.lang.Object
中断和虚假唤醒是可能产生的,所以要用loop循环,if只判断一次,while是只要唤醒就要拉回来再判断一次。if换成while

用Lock实现生成者消费者模型

Condition 接口

condition可以理解为条件队列。当一个线程在调用了await方法后,直到线程等待的某个条件为真的时候为会被唤醒。这种方式为线程提供了更加简单的等待/通知模式。Condition一般都是作为Lock的内部实现。

  • await():造成当前线程在接到信号或被中断之前一直处于等待状态。
  • await(long time, TimeUint unit):造成当前线程在接到信号、被中断或到达等待时间之前一直处于等待状态。
  • awaitNanos(long nanosTimeout):造成当前线程在接到信号、被中断或达到指定等待时间之前一直处于等待状态。返回值表示剩余时间,如果在nanosTimesout之前唤醒,那么返回值= nanosTimeout - 消耗时间,如果返回值 <=0,则可以认定它已经超时了。
  • awaitUninterruptibly():造成当前线程在接到信号之前一直处于等待状态。【该方法对中断不敏感】
  • awaitUntil(Date deadline):造成当前线程在接到信号、被中断或到达指定最后期限之前一直处于等待状态。如果没有到指定时间就被通知,则返回true,否则表示到了指定时间,返回值false。
  • singal():唤醒一个等待线程,该线程从等待方法返回前必须获得与Condition相关的锁。
  • singalAll():唤醒所有等待线程。能够从等待方法返回的线程必须获得与Condition相关的锁。
class AirContainer{
    private int number = 0;
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();
    public void increment() throws InterruptedException {
        lock.lock();
        try{
            while (number != 0){
                condition.await();
            }
            //2.干活
            number++;
            //3.通知
            System.out.println(Thread.currentThread().getName()+"\t"+number);
            condition.signalAll();
        }finally {
            lock.unlock();
        }

    }

    public void decreament() throws InterruptedException {
        lock.lock();
        try{
            while (number == 0){
                condition.await();
            }
            //2.干活
            number--;
            //3.通知
            System.out.println(Thread.currentThread().getName()+"\t"+number);
            condition.signalAll();
        }finally {
            lock.unlock();
        }

    }
}


public class LockThreadWaitNotifyDemo {

    public static void main(String args[]){
        AirContainer airContainer = new AirContainer();
        new Thread(()->{
            try {
                for (int i = 0; i < 10; i++) {
                    airContainer.increment();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"A").start();

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

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

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

}

线程间定制化调用通信例子

/**
 * 
 * @Description: 
 * 多线程之间按顺序调用,实现A->B->C
 * 三个线程启动,要求如下:
 * 
 * AA打印5次,BB打印10次,CC打印15次
 * 接着
 * AA打印5次,BB打印10次,CC打印15次
 * ......来10轮  
 *
 */
/**
 * 解决思路
 * 1、有顺序通知,需要有标识位
 *
 * 2、有一个锁Lock,3把钥匙Condition
 *  
 * 3、判断标志位
 * 
 * 4、修改标志位,通知下一个
**/
class PrintResource{
    private int number = 0;
    private Lock lock = new ReentrantLock();
    private Condition condition1 = lock.newCondition();
    private Condition condition2 = lock.newCondition();
    private Condition condition3 = lock.newCondition();

    public void printA(){
        lock.lock();
        try{
            while (number != 0){
                condition1.await();
            }
            for (int i = 0; i < 5 ; i++) {
                System.out.println(Thread.currentThread().getName());
            }
            number = 1;
            condition2.signal(); //用下一把唤醒
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public void printB(){
        lock.lock();
        try{
            while(number != 1){
                condition2.await();
            }
            for (int i = 0; i < 10 ; i++) {
                System.out.println(Thread.currentThread().getName());
            }
            number = 2;
            condition3.signal();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public void printC(){
        lock.lock();
        try{
            while(number != 2){
                condition3.await();
            }
            for (int i = 0; i < 15 ; i++) {
                System.out.println(Thread.currentThread().getName());
            }
            number = 0;
            condition1.signal();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
}



public class TreadLoop {
    public static void main(String args[]){
        PrintResource printResource = new PrintResource();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                printResource.printA();
            }
        },"A").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                printResource.printB();
            }
        },"B").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                printResource.printC();
            }
        },"C").start();
    }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值