ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

JAVA synchronized关键字涉及的Monitor对象中 EntryList和WaitSet工作机制

JAVA synchronized关键字涉及的Monitor对象中 EntryList和WaitSet工作机制

在Java的synchronized同步机制中,Monitor对象的EntryList和WaitSet是两个关键队列,它们分别管理不同状态的线程。下面我将详细解释它们的工作原理,并提供代码示例说明。

EntryList(锁竞争队列)

作用机制

EntryList保存了所有等待获取锁的线程。当锁被某个线程持有时,其他尝试获取该锁的线程会被放入EntryList中,处于BLOCKED状态。

工作流程

  1. 线程尝试获取锁时发现锁已被持有
  2. 线程被放入EntryList等待
  3. 当持有锁的线程释放锁时,JVM会从EntryList中选择一个线程唤醒
  4. 被唤醒的线程尝试获取锁

代码示例

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()方法的线程。这些线程已经获得了锁,但主动释放锁并等待被唤醒。

工作流程

  1. 线程获得锁后调用wait()方法
  2. 线程释放锁并进入WaitSet,处于WAITING状态
  3. 其他线程调用notify()/notifyAll()时,线程从WaitSet转移到EntryList
  4. 当再次获得锁时,从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的区别

特性EntryListWaitSet
线程状态BLOCKEDWAITING
进入方式竞争锁失败自动进入主动调用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. 线程1获得锁后主动wait()进入WaitSet
  2. 线程2从EntryList中获得锁并notify()唤醒线程1
  3. 线程2释放锁后,线程1从WaitSet转移到EntryList并重新获得锁
  4. 最后线程3获得锁
返回列表