在Java的synchronized
同步机制中,Monitor对象的EntryList和WaitSet是两个关键队列,它们分别管理不同状态的线程。下面我将详细解释它们的工作原理,并提供代码示例说明。
EntryList(锁竞争队列)
作用机制
EntryList保存了所有等待获取锁的线程。当锁被某个线程持有时,其他尝试获取该锁的线程会被放入EntryList中,处于BLOCKED状态。
工作流程
- 线程尝试获取锁时发现锁已被持有
- 线程被放入EntryList等待
- 当持有锁的线程释放锁时,JVM会从EntryList中选择一个线程唤醒
- 被唤醒的线程尝试获取锁
代码示例
public class EntryListDemo {private static final Object lock = new Object();public static void main(String[] args) {// 线程1先获取锁new Thread(() -> {synchronized (lock) {System.out.println("线程1获取锁");try {Thread.sleep(3000); // 模拟长时间操作} catch (InterruptedException e) {e.printStackTrace();}System.out.println("线程1释放锁");}}).start();// 线程2稍后尝试获取锁new Thread(() -> {try {Thread.sleep(100); // 确保线程1先获取锁} catch (InterruptedException e) {e.printStackTrace();}synchronized (lock) { // 这里会被放入EntryList等待System.out.println("线程2获取锁");}}).start();}
}
输出结果:
线程1获取锁
(等待约3秒)
线程1释放锁
线程2获取锁
WaitSet(等待队列)
作用机制
WaitSet保存了调用了wait()
方法的线程。这些线程已经获得了锁,但主动释放锁并等待被唤醒。
工作流程
- 线程获得锁后调用
wait()
方法 - 线程释放锁并进入WaitSet,处于WAITING状态
- 其他线程调用
notify()
/notifyAll()
时,线程从WaitSet转移到EntryList - 当再次获得锁时,从
wait()
调用处继续执行
代码示例
public class WaitSetDemo {private static final Object lock = new Object();private static volatile boolean condition = false;public static void main(String[] args) throws InterruptedException {// 等待线程Thread waitingThread = new Thread(() -> {synchronized (lock) {System.out.println("等待线程获得锁");while (!condition) {try {System.out.println("等待线程调用wait()");lock.wait(); // 释放锁并进入WaitSetSystem.out.println("等待线程被唤醒");} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("等待线程完成工作");}});// 通知线程Thread notifyingThread = new Thread(() -> {synchronized (lock) {System.out.println("通知线程获得锁");condition = true;lock.notify(); // 唤醒WaitSet中的一个线程System.out.println("通知线程已发送通知");}});waitingThread.start();Thread.sleep(500); // 确保等待线程先执行notifyingThread.start();}
}
输出结果:
等待线程获得锁
等待线程调用wait()
通知线程获得锁
通知线程已发送通知
等待线程被唤醒
等待线程完成工作
EntryList和WaitSet的区别
特性 | EntryList | WaitSet |
---|---|---|
线程状态 | BLOCKED | WAITING |
进入方式 | 竞争锁失败自动进入 | 主动调用wait()进入 |
唤醒方式 | 锁释放时自动唤醒 | 必须通过notify()/notifyAll() |
锁的状态 | 从未获得过锁 | 曾获得锁但主动释放 |
转移目标 | 获得锁后进入运行状态 | 被唤醒后进入EntryList |
完整生命周期示例
public class MonitorLifecycleDemo {private static final Object lock = new Object();public static void main(String[] args) {// 线程1:演示wait()和notify()new Thread(() -> {synchronized (lock) {System.out.println("线程1获得锁");try {System.out.println("线程1调用wait()进入WaitSet");lock.wait(); // 进入WaitSetSystem.out.println("线程1被唤醒,重新获得锁");} catch (InterruptedException e) {e.printStackTrace();}System.out.println("线程1释放锁");}}).start();// 线程2:竞争锁进入EntryListnew Thread(() -> {try {Thread.sleep(100); // 确保线程1先获得锁} catch (InterruptedException e) {e.printStackTrace();}synchronized (lock) {System.out.println("线程2从EntryList中被选中获得锁");System.out.println("线程2调用notify()唤醒WaitSet中的线程");lock.notify(); // 唤醒线程1,线程1进入EntryListtry {Thread.sleep(1000); // 保持锁一段时间} catch (InterruptedException e) {e.printStackTrace();}System.out.println("线程2释放锁");}}).start();// 线程3:展示EntryList中的竞争new Thread(() -> {try {Thread.sleep(200); // 确保线程2已获得锁} catch (InterruptedException e) {e.printStackTrace();}synchronized (lock) {System.out.println("线程3获得锁");}}).start();}
}
输出结果:
线程1获得锁
线程1调用wait()进入WaitSet
线程2从EntryList中被选中获得锁
线程2调用notify()唤醒WaitSet中的线程
(等待约1秒)
线程2释放锁
线程1被唤醒,重新获得锁
线程1释放锁
线程3获得锁
这个示例展示了:
- 线程1获得锁后主动wait()进入WaitSet
- 线程2从EntryList中获得锁并notify()唤醒线程1
- 线程2释放锁后,线程1从WaitSet转移到EntryList并重新获得锁
- 最后线程3获得锁