在Java中,监视器(Monitor)是一种同步机制,用于实现多线程环境下的互斥访问和线程协作,它本质上是一种对象,通过内置的锁(也称为监视器锁)来确保同一时间只有一个线程可以访问共享资源,本文将详细介绍Java中如何使用监视器,包括内置锁机制、同步方法与同步代码块、wait/notify机制以及高级应用场景。

Java监视器的基本概念
Java中的监视器与对象绑定,每个对象都有一个唯一的监视器,当线程试图访问对象的同步代码时,必须先获取该对象的监视器锁,一旦获取锁,其他线程将被阻塞,直到锁被释放,这种机制可以有效防止多线程并发访问导致的数据不一致问题,监视器的核心特性包括互斥性(同一时间只有一个线程持有锁)和可见性(锁的释放会刷新主内存,确保线程间数据同步)。
同步方法:使用s修饰符
同步方法是最简单的监视器使用方式,通过在方法声明前添加synchronized关键字,可以将整个方法体置于监视器的控制之下。
public synchronized void increment() {
count++;
}
在上述代码中,increment方法被声明为同步方法,当线程调用此方法时,会自动获取当前对象的监视器锁,方法执行完毕后自动释放锁,需要注意的是,静态同步方法获取的是类对象的监视器锁,而非实例对象的锁。
同步代码块:更灵活的锁定范围
同步代码块允许开发者精确控制锁定的范围,从而减少锁的持有时间,提高并发性能,其语法为:
synchronized (锁对象) {
// 需要同步的代码
}
锁对象可以是任意类型的实例,但通常推荐使用共享资源本身或专门的锁对象。

public void updateList(List<String> list, String item) {
synchronized (list) {
list.add(item);
}
}
相比同步方法,同步代码块可以避免锁定整个方法,从而减少线程阻塞的可能性,如果方法中包含大量非同步逻辑,将其放在同步代码块外可以显著提升性能。
wait/notify机制:线程协作
监视器不仅支持互斥访问,还提供了线程协作的机制,主要通过wait()、notify()和notifyAll()方法实现,这些方法必须在同步代码块或同步方法中调用,否则会抛出IllegalMonitorStateException。
- wait():使当前线程释放锁并进入等待状态,直到其他线程调用
notify()或notifyAll()唤醒它。 - notify():随机唤醒一个正在等待的线程,该线程将重新尝试获取锁。
- notifyAll():唤醒所有等待的线程,它们将竞争获取锁。
典型应用场景是生产者-消费者模型:
public class Buffer {
private int[] buffer;
private int count, in, out;
public Buffer(int size) {
buffer = new int[size];
}
public synchronized void put(int value) throws InterruptedException {
while (count == buffer.length) {
wait(); // 缓冲区满时等待
}
buffer[in] = value;
in = (in + 1) % buffer.length;
count++;
notifyAll(); // 通知消费者
}
public synchronized int take() throws InterruptedException {
while (count == 0) {
wait(); // 缓冲区空时等待
}
int value = buffer[out];
out = (out + 1) % buffer.length;
count--;
notifyAll(); // 通知生产者
return value;
}
}
在上述代码中,put和take方法通过wait()和notifyAll()实现线程间的协调,避免忙等待(busy-waiting)资源浪费。
ReentrantLock:更强大的锁机制
虽然synchronized是Java内置的监视器机制,但java.util.concurrent.locks.ReentrantLock提供了更灵活的锁定功能,包括可中断的锁获取、公平锁以及尝试锁定等。

import java.util.concurrent.locks.ReentrantLock;
public class Counter {
private final ReentrantLock lock = new ReentrantLock();
private int count = 0;
public void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock(); // 确保锁被释放
}
}
}
与synchronized相比,ReentrantLock需要手动释放锁,但提供了更细粒度的控制,适合复杂同步场景。
监视器的最佳实践
- 避免锁粒度过粗:尽量减少同步代码块的范围,降低线程竞争。
- 防止死锁:避免多个线程以不同顺序获取多个锁,例如通过固定锁的获取顺序。
- 使用
tryLock:在ReentrantLock中,优先使用tryLock()避免无限等待。 - 优先选择
notifyAll():notify()可能导致线程饥饿,notifyAll()更安全但可能影响性能。 - 注意锁对象的选择:避免使用字符串常量或易变对象作为锁,推荐使用
final修饰的私有对象。
Java监视器是多线程编程的核心工具,通过synchronized和wait/notify机制实现线程的安全协作,开发者应根据实际场景选择同步方法、同步代码块或ReentrantLock,并遵循最佳实践以优化性能,合理使用监视器可以有效避免并发问题,构建高效的多线程应用程序。

















