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

 

 

上述有三个队列,这些队列中的节点,都是线程包装成的 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

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

 

posted @ 2020-11-06 15:47  执生  阅读(343)  评论(0编辑  收藏  举报