什么是监视器(Monitor)?
在Java中,监视器(Monitor)是用来实现线程同步的一种机制。每个Java对象都有一个与之关联的监视器,线程可以通过synchronized
关键字来获取和释放对象的监视器。监视器的主要作用是确保在同一时刻只有一个线程可以执行同步块或同步方法,从而实现线程的互斥访问。
监视器的组成部分
监视器通常包含以下三个关键部分:
- 入口集(Entry Set):等待获取监视器锁的线程集合。
- 所有者线程(Owner Thread):当前持有监视器锁的线程。
- 等待集(Wait Set):调用了
wait()
方法并进入等待状态的线程集合。
线程等待的地方
- 入口集(Entry Set):线程在尝试进入同步块或同步方法时,如果无法获取监视器锁,它们会进入入口集等待。这些线程处于阻塞状态,等待获取监视器锁。
- 等待集(Wait Set):线程在调用
wait()
方法后,会释放监视器锁并进入等待集。这些线程处于等待状态,直到被其他线程通过notify()
或notifyAll()
方法唤醒。
线程状态转换示意图
以下是线程在不同状态之间转换的过程示意图:
- 新建状态(New):线程被创建,但尚未启动。
- 可运行状态(Runnable):线程已经启动,可以运行但不一定正在运行。
- 阻塞状态(Blocked):线程在入口集中,等待获取监视器锁。
- 等待状态(Waiting):线程在等待集中,等待其他线程通过
notify()
或notifyAll()
唤醒。 - 超时等待状态(Timed Waiting):线程在等待集中,等待特定时间后被唤醒。
- 终止状态(Terminated):线程已经结束执行。
示例代码解释
我们通过一个示例代码来解释线程在不同状态之间的转换:
public class MonitorExample { private static final Object lock = new Object(); public static void main(String[] args) { Thread thread1 = new Thread(() -> { synchronized (lock) { try { System.out.println("Thread 1: Acquired lock, entering wait state."); lock.wait(); System.out.println("Thread 1: Woken up, reacquired lock."); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } }); Thread thread2 = new Thread(() -> { synchronized (lock) { System.out.println("Thread 2: Acquired lock, notifying."); lock.notify(); System.out.println("Thread 2: Notified, releasing lock."); } }); thread1.start(); try { Thread.sleep(100); // Ensure thread1 starts first and enters wait state } catch (InterruptedException e) { Thread.currentThread().interrupt(); } thread2.start(); } }
过程解释
- Thread 1 获取监视器锁:
Thread 1
进入同步块并获取监视器锁。- 调用
lock.wait()
方法,Thread 1
释放监视器锁并进入等待集。
- Thread 2 获取监视器锁:
Thread 2
进入同步块并获取监视器锁(此时Thread 1
已在等待集)。- 调用
lock.notify()
方法,唤醒等待集中的一个线程(即Thread 1
)。
- Thread 2 释放监视器锁:
Thread 2
退出同步块,释放监视器锁。
- Thread 1 重新获取监视器锁:
- 被唤醒的
Thread 1
从等待集中移动到锁池,重新竞争获取监视器锁。 Thread 1
成功获取监视器锁后,从wait()
方法返回,继续执行后续代码。
总结
- 监视器(Monitor):用于实现线程同步,每个Java对象都有一个监视器。
- 入口集(Entry Set):线程在尝试进入同步块或同步方法时,如果无法获取监视器锁,会进入入口集等待。
- 等待集(Wait Set):线程在调用
wait()
方法后,会释放监视器锁并进入等待集,等待被唤醒。 - 状态转换:线程在不同状态之间转换,包括新建、可运行、阻塞、等待、超时等待和终止状态。