上述有三个队列,这些队列中的节点,都是线程包装成的 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
也就是 曾经获得过锁的线程 被唤醒后 优先得到锁