• 重量级锁的加锁-等待-撤销流程


    上述有三个队列,这些队列中的节点,都是线程包装成的 ObjectWaiter

    在默认策略情况下:

    1.entry_list 中的 节点是等待被唤醒的节点,持有重量级锁的线程执行 exit 方法(Java层面:退出上述 synchronized区或调用 wait()方法 会调用 (C++层面)exit方法),exit方法会唤醒 本队列的头节点(unpark),避免惊群

    2.cxq_list 中的 节点是 获取锁失败后的线程的节点,exit 方法会检查 entry_list 是否为空,如果是空的,就会从 cxq_list 中拿 节点,放到 entry_list,放入顺序默认是 把 cxq_list 直接插到 entry_list 后面

    3.wait_set 是调用 java 层面的 wait() 方法 的线程 该待的地方,需要注意的是 ,只有 notify 和 notifyAll 能够把 wait_set 中的节点 移入到 cxq_list (默认是把 cxq_list 插入到 wait_set 后面,wait_set 做为新的 cxq_list)

    下面这个案例中,执行 a 的线程不会被唤醒。因为执行 b 的线程退出 sychronized 区只是 调用了 c++ 层面的 exit 方法,没有把 wait_set 中的 执行 a 的线程 放入到 cxq_list

    调用 notify 和 notifyAll 方法 不会让  wait_set 里的线程 被 unpark ,只是让他们移入 cxq_list。 

         Runnable a = () -> {
                synchronized (lock) {
                    System.out.println("working");
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("finalize");
                }
            };
    
            Runnable b = () -> {
                synchronized (lock) {
                    System.out.println("doSomething");
                }
                System.out.println("done");
            };
            new Thread(a).start();
            Thread.sleep(1000);
            new Thread(b).start();

    默认的 cxq_list 进入 entry_list 的方式:

    默认的 wait_set 节点进入 cxq_list 的方式(notifyAll)

    默认的 wait_set 节点进入 cxq_list 的方式(notify)

     

    也就是,如果有一个线程 a 先进入 synchronized , 但是调用了 wait,这是线程 b 进入了 synchronized,b还在synchronized中执行,c线程又进来了。

    此时 a 在 wait_set ,b 不在任何队列,c 在 cxq_list ,假如 b 调用 notify,会把 a 插到 c 前面,也就是 b 退出synchronized的时候,会唤醒 a,a退出之后再唤醒 c

    也就是 曾经获得过锁的线程 被唤醒后 优先得到锁

  • 相关阅读:
    非空约束
    leetcode208
    leetcode207
    leetcode395
    leetcode116
    leetcode105
    leetcode131
    leetcode73
    leetcode200
    leetcode17
  • 原文地址:https://www.cnblogs.com/lqlqlq/p/13937231.html
Copyright © 2020-2023  润新知