多线程之等待唤醒机制

等待唤醒机制总的来说共有三种

1.被弃用的suspend/resume
2.wait/notify机制
3.park/unpark机制

1.被弃用的suspend/resume

调用suspend挂起目标线程,通过resume可以恢复线程执行
被弃用的主要原因是因为: 容易写出死锁的代码
比如:

public void test_suspend() throws Exception {
        //开启一个线程,代表小朋友
        Thread consumerThread = new Thread(new Runnable() {
            @Override
            public void run() {
                if (iceCream == null) {
                    System.out.println("没有冰激凌,等待...");
                    try {
                        Thread.sleep(6000L);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    Thread.currentThread().suspend();  //挂起
                }
                System.out.println("小朋友买到冰激凌");
            }
        });
        consumerThread.start();

        Thread.sleep(3000L);    // 3秒之后
        iceCream = new Object();      //店员做好了冰激凌

        System.out.println("通知小朋友");
        consumerThread.resume();    //通知小朋友
    }

执行后,可以看到,线程死锁了
在这里插入图片描述

2.wait/notify机制

  • wait方法可使当前线程进入等待状态, 加入到该对象的等待集合中,而且会随之将该线程持有的锁给释放掉
  • notify/notifyAll方法,唤醒一个/所有的正在等待对象锁的线程

注意点:

  1. wait自动解锁对顺序有要求, 如果在notify被调用之后才开始wait方法的调用,线程就会永远的处于WAITNG状态了
  2. wait/notify方法必须在同步代码块中进行调用,否则会抛出异常IllegalMonitorStateException

正常使用方式:

	/**
     * 正常的wait/notify
     * @throws Exception
     */
    public void test1_normal() throws Exception {
        //开启一个线程
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (iceCream == null) { //若没有冰激凌
                    synchronized (WaitNotify.class) {
                        System.out.println("小朋友拿到锁。。。");
                        try {
                            System.out.println("没有冰激凌,等待...");
                            WaitNotify.class.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
                System.out.println("小朋友买到冰激凌");
            }
        }).start();

        Thread.sleep(3000L);
        //做了一个冰激凌
        iceCream = new Object();

        synchronized (WaitNotify.class) {
            System.out.println("拿到锁。。。");
            WaitNotify.class.notifyAll();
            System.out.println("通知小朋友");
        }
    }

死锁示例代码:

	/**
     * 死锁
     * @throws Exception
     */
    public void test2_DeadLock() throws Exception {
        //开启一个线程,代表小朋友
        new Thread(new Runnable() {
            @Override
            public void run() {
                if (iceCream == null) {
                    try {
                        Thread.sleep(5000L);
                        System.out.println("没有冰激凌,等待...");
                        synchronized (WaitNotify.class){
                            WaitNotify.class.wait();
                        }
                    } catch (InterruptedException e1) {
                        e1.printStackTrace();
                    }
                }
                System.out.println("小朋友买到冰激凌");
            }
        }).start();

        Thread.sleep(3000L);
        // 店员做了一个冰激凌
        iceCream = new Object();

        //通知小朋友
        synchronized (WaitNotify.class){
            // notifyAll方法在wait方法之前执行,会造成永久等待
            WaitNotify.class.notifyAll();
        }
        System.out.println("通知小朋友");
    }

3.park/unpark机制

  • park方法等待“许可”, unpark方法提供“许可”
  • 调用unpark之后,再调用park,线程会直接运行
  • 提前调用的unpark不叠加,连续多次调用unpark后,第一次调用park后会拿到“许可”直接运行,后续调用会进入等待。

和wait/notify不同的点在于,park/unpark机制类似"通行证",如果提前拿到了"通行证",后面才进行"限制"的话,也可以正常使用"通行证"

正常使用方式:

/**
     * 正常的park/unpark
     * @throws Exception
     */
    public void test1_normal() throws Exception {
        //开启一个线程,代表小朋友
        Thread consumerThread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (iceCream == null) {    
                    System.out.println("没有冰激凌,等待...");
                    LockSupport.park();
                }
                System.out.println("小朋友买到冰激凌");
            }
        });
        consumerThread.start();

        Thread.sleep(3000L);
        iceCream = new Object();
        
        // 通知小朋友
        LockSupport.unpark(consumerThread);

        System.out.println("通知小朋友");
    }

伪唤醒

1.是指线程并非因为notify、notifyall、unpark等api调用而意外唤醒,更底层原因导致的情况
2.官方建议应该在循环中检查等待条件而不是用if

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值