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

  • 相关阅读:
    hdu 6702 ^&^ 位运算
    hdu 6709 Fishing Master 贪心
    hdu 6704 K-th occurrence 二分 ST表 后缀数组 主席树
    hdu 1423 Greatest Common Increasing Subsequence 最长公共上升子序列 LCIS
    hdu 5909 Tree Cutting FWT
    luogu P1588 丢失的牛 宽搜
    luogu P1003 铺地毯
    luogu P1104 生日
    luogu P1094 纪念品分组
    luogu P1093 奖学金
  • 原文地址:https://www.cnblogs.com/dalianpai/p/14204522.html
Copyright © 2011-2022 走看看