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

发布于:2025-07-09 ⋅ 阅读:(21) ⋅ 点赞:(0)

在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(); // 释放锁并进入WaitSet
                        System.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(); // 进入WaitSet
                    System.out.println("线程1被唤醒,重新获得锁");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("线程1释放锁");
            }
        }).start();
        
        // 线程2:竞争锁进入EntryList
        new 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进入EntryList
                try {
                    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获得锁

网站公告

今日签到

点亮在社区的每一天
去签到