zoukankan      html  css  js  c++  java
  • 面试题:3种线程阻塞唤醒的对比

    3种线程阻塞唤醒

    wait/notify

    /**
     * @author WGR
     * @create 2020/12/29 -- 0:28
     */
    public class Test6 {
    
        private static Object objectLock = new Object();
    
        public static void main(String[] args) {
            new Thread(() ->{
                synchronized (objectLock){
                    System.out.println(Thread.currentThread().getName()+"----come in");
                    try {
                        objectLock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"----被唤醒");
                }
            },"A").start();
    
            new Thread(() ->{
                synchronized (objectLock){
                    objectLock.notify();
                    System.out.println(Thread.currentThread().getName()+"----通知");
                }
            },"B").start();
        }
    }
    

    image-20201229003423631

    当A先睡眠的时候,B先去唤醒

    image-20201229003540263

    当去掉锁的代码块的时候

    image-20201229003713232

    image-20201229003910596

    • Owner 线程发现条件不满足,调用 wait 方法,即可进入 WaitSet 变为 WAITING 状态
    • BLOCKED 和 WAITING 的线程都处于阻塞状态,不占用 CPU 时间片
    • BLOCKED 线程会在 Owner 线程释放锁时唤醒
    • WAITING 线程会在 Owner 线程调用 notify 或 notifyAll 时唤醒,但唤醒后并不意味者立刻获得锁,仍需进入EntryList 重新竞争

    await/signal

    它的演示结果和上面一样就不贴出来了

    public class Test6 {
    
        private static Lock lock = new ReentrantLock();
        private static Condition condition = lock.newCondition();
    
        public static void main(String[] args) {
    
    
            new Thread(() ->{
                lock.lock();
                   try {
                       try {
                           System.out.println(Thread.currentThread().getName()+"----come in");
                           condition.await();
                       } catch (InterruptedException e) {
                           e.printStackTrace();
                       }
                       System.out.println(Thread.currentThread().getName()+"----被唤醒");
                   }finally {
                       lock.unlock();
                   }
    
            },"A").start();
    
            new Thread(() ->{
                lock.lock();
                try{
                    condition.signal();
                    System.out.println(Thread.currentThread().getName()+"----通知");
                }finally {
                    lock.unlock();
                }
    
            },"B").start();
        }
    }
    

    await 流程
    每个条件变量其实就对应着一个等待队列,其实现类是 ConditionObject,开始 Thread-0 持有锁,调用 await,进入 ConditionObject 的 addConditionWaiter 流程,创建新的 Node 状态为 -2(Node.CONDITION),关联 Thread-0,加入等待队列尾部

    image-20201229004156847

    接下来进入 AQS 的 fullyRelease 流程,释放同步器上的锁

    image-20201229004213744

    unpark AQS 队列中的下一个节点,竞争锁,假设没有其他竞争线程,那么 Thread-1 竞争成功

    image-20201229004234391

    park 阻塞 Thread-0

    image-20201229004253020

    signal 流程
    假设 Thread-1 要来唤醒 Thread-0

    image-20201229004346041

    进入 ConditionObject 的 doSignal 流程,取得等待队列中第一个 Node,即 Thread-0 所在 Node

    image-20201229004416130

    执行 transferForSignal 流程,将该 Node 加入 AQS 队列尾部,将 Thread-0 的 waitStatus 改为 0,Thread-3 的waitStatus 改为 -1

    image-20201229004445201

    park/unpark

    /**
     * @author WGR
     * @create 2020/12/29 -- 1:02
     */
    public class Test {
        public static void main(String[] args) {
            Thread t1 = new Thread(() -> {
                System.out.println("start...");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("park...");
                LockSupport.park();
                System.out.println("resume...");
            },"t1");
            t1.start();
            System.out.println("unpark...");
            LockSupport.unpark(t1);
        }
    }
    

    image-20201229010441291

    特点

    • wait,notify 和 notifyAll 必须配合 Object Monitor 一起使用,而 park,unpark 不必
    • park & unpark 是以线程为单位来【阻塞】和【唤醒】线程,而 notify 只能随机唤醒一个等待线程,notifyAll是唤醒所有等待线程,就不那么【精确】
    • park & unpark 可以先 unpark,而 wait & notify 不能先 notify

    image-20201229010815103

    1. 当前线程调用 Unsafe.park() 方法
    2. 检查 _counter ,本情况为 0,这时,获得 _mutex 互斥锁
    3. 线程进入 _cond 条件变量阻塞
    4. 设置 _counter = 0

    image-20201229010840158

    1. 调用 Unsafe.unpark(Thread_0) 方法,设置 _counter 为 1
    2. 唤醒 _cond 条件变量中的 Thread_0
    3. Thread_0 恢复运行
    4. 设置 _counter 为 0

    image-20201229010859017

    1. 调用 Unsafe.unpark(Thread_0) 方法,设置 _counter 为 1
    2. 当前线程调用 Unsafe.park() 方法
    3. 检查 _counter ,本情况为 1,这时线程无需阻塞,继续运行
    4. 设置 _counter 为 0

    常见面试题

    image-20201228225823838

  • 相关阅读:
    就南通大学教务管理系统微信公众号的个人看法
    设计一款给爸爸妈妈用的手机
    .NET中DLL“没有可放置在工具箱的组件”—FreeTextBox
    如何查看.Net源代码vs版本号以及C#项目中各文件的含义
    .NET在后置代码中输入JS提示语句(背景不会变白)
    Docker技术学习
    学习linux基本命令
    性能相关英文单词学习
    常见Http状态码的含义
    学习siege实战
  • 原文地址:https://www.cnblogs.com/dalianpai/p/14204522.html
Copyright © 2011-2022 走看看