线程间通信方式及常用API

想要实现多线程之间的协同,如:线程执行先后顺序,获取某个线程执行的结果等等。涉及到多线程之间的互相通信,分为下面四类:

  • 文件共享

  • 网络共享

  • 共享变量
  • JDK提供的线程协调API(suspend/rsume.wati/notify,park/unpakr)

suspend/resume (已被JDK弃用),该方法容易引起死锁,死锁原因如下:

/**
* suspend() 和 resume() 方法的调用顺序引起死锁
*/
public void suspendResumeTest03() {
    Thread t1 = new Thread(() -> {
        while (object == null) {
            System.out.println("1.等待包子生产");
            try {
                Thread.sleep(5000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }


            Thread.currentThread().suspend();




        }


        System.out.println("3.买到包子了");
    });
    t1.start();
    try {
        Thread.sleep(2000L);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    object = new Object();
    t1.resume();




    System.out.println("2.生产包子");
}

   2. suspend()方法不会释放锁

/**
* 对象锁操作 --会导致死锁 --suspend()不会释放锁
*/
public void suspendResumeTest01() {
    Thread t1 = new Thread(() -> {
        while (object == null) {
            synchronized (this) {
                System.out.println("1.等待包子生产");
                Thread.currentThread().suspend();
            }


        }


        System.out.println("3.买到包子了");
    });
    t1.start();
    try {
        Thread.sleep(2000L);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    object = new Object();
    synchronized (this) {
        t1.resume();


    }
    System.out.println("2.生产包子");
}

3.正确执行代码如下:

/**
* suspend 和 resume 正常演示
*/
public void suspendResumeTest() {
    Thread t1 = new Thread(() -> {
        while (object == null) {
            System.out.println("1.等待包子生产");
            Thread.currentThread().suspend();


        }


        System.out.println("3.买到包子了");
    });
    t1.start();
    try {
        Thread.sleep(2000L);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    object = new Object();
    t1.resume();
    System.out.println("2.生产包子");
}

wait/notify:方法只能由同一对象锁的持有者线程调用,也就是写在同步块里面,否则会抛出IllegalMonitorStateException异常。

wait方法会导致当前线程等待,加入该对象的等待集合中,并且放弃当前持有的对象锁。

notify/notifyAll方法唤醒一个或所有正在等待这个对象锁的线程。

注意:虽然wait会自动解锁,但是对顺序有要求,如果在notify被调用之后,才开始wait方法的调用,线程就会永远处于WAITING状态。

正确代码

/**
* 正确地处理方式
*/
public void watiNotifyTest() {
    new Thread(() -> {


        while (object == null) {
            System.out.println("1.等待包子生产");
            try {
                synchronized (this) {
                    this.wait();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("3.成功买到包子");
    }).start();
    try {
        Thread.sleep(200L);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    object=new Object();
    synchronized (this){
        this.notify();
    }
    System.out.println("2.包子生产完成");
}

死锁代码

/**
* 虽然wait() 和notify() 会释放锁,但是有先后顺序
*/
public void waitNotifyTest02(){
    new Thread(() -> {


        while (object == null) {
            System.out.println("1.等待包子生产");
            try {
                Thread.sleep(3000L);
                synchronized (this) {
                    this.wait();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("3.成功买到包子");
    }).start();
    try {
        Thread.sleep(200L);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    object=new Object();
    synchronized (this){
        this.notify();
    }
    System.out.println("2.包子生产完成");
}

pack/unpack机制:线程调用park则等待“许可”,unpack方法为指定线程提供“许可”。

不要求park和unpark方法的调用顺序,多次调用unpark之后,再调用park,线程会直接运行,但不会叠加。

也就是说,连续多次调用park方法,第一次会拿到“许可”直接运行,后续调用会进入等待。

park方法不会释放锁,也可能会引起死锁操作。

正确代码

/**
* park() 和 unpacke() 对调用的方法的先后顺序没有问题。
*/
public void  parkUnpackTest(){
    Thread t1=new Thread(()->{
       while (object==null){
           System.out.println("1.等待包子生产");
           try {
               Thread.sleep(2000L);
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
           LockSupport.park();
       }
        System.out.println("3.成功买到包子");
    });
    t1.start();
    try {
        Thread.sleep(200L);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    object=new Object();
    LockSupport.unpark(t1);
    System.out.println("2.店家成功生产包子");
}

错误代码

/**
* 会产生死锁 parK() 方法不会释放锁
*/
public void  parkUnpackTest02(){
    Thread t1=new Thread(()->{
        while (object==null){
            System.out.println("1.等待包子生产");
            synchronized (this){
                LockSupport.park();
            }


        }
        System.out.println("3.成功买到包子");
    });
    t1.start();
    try {
        Thread.sleep(200L);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    object=new Object();
    synchronized (this){
        LockSupport.unpark(t1);
    }


    System.out.println("2.店家成功生产包子");
}

伪唤醒

警告!之前的代码中如果用把while改成if语句来判断是否进入等待状态是错误的

官方建议应该在循环中检查等待条件,原因是处于等待状态的线程可能会收到错误警报和伪唤醒,

如果不在循环中检查等待条件,程序就会在没有满足结束条件的情况下退出。

伪唤醒是指线程并非因为notify,notifyall,unpark等api调用而唤醒,是更底层原因导致的。

线程间通信常见的模式:生产者---消费者 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值