### wait, notify等线程知识详解
#### 一、引言
在计算机程序设计中,尤其是在多线程环境中,线程间的同步与通信是保证程序正确性和效率的关键因素之一。`wait` 和 `notify` 是Java语言提供的原生方法,用于解决线程之间的同步问题。本文将详细介绍这些方法的基本原理、使用场景以及最佳实践。
#### 二、基本概念
在Java中,`wait()` 和 `notify()` 方法用于实现线程间的同步。它们属于 `Object` 类的方法,这意味着所有对象都可以作为同步锁来使用。
##### 1. wait()
`wait()` 方法用于使当前线程进入等待状态,直到其他线程调用该对象的 `notify()` 或 `notifyAll()` 方法来唤醒它。当调用 `wait()` 方法时,当前线程会释放锁,并等待其他线程唤醒。`wait()` 方法通常在一个循环中使用,检查某个特定条件是否满足。
**示例代码:**
```java
synchronized (obj) {
while (!condition) {
obj.wait();
}
// do something
}
```
##### 2. notify()
`notify()` 方法用于唤醒正在等待该对象锁的一个线程。需要注意的是,`notify()` 只能唤醒一个线程,具体唤醒哪一个线程由JVM决定。
**示例代码:**
```java
synchronized (obj) {
condition = true;
obj.notify();
}
```
##### 3. notifyAll()
`notifyAll()` 方法用于唤醒所有正在等待该对象锁的线程。这通常用于所有等待线程都需要被唤醒的情况。
**示例代码:**
```java
synchronized (obj) {
condition = true;
obj.notifyAll();
}
```
#### 三、注意事项
1. **锁的获取与释放:**
- 调用 `wait()`, `notify()`, `notifyAll()` 必须在 `synchronized` 块中,即必须先获得锁。
- 当线程调用 `wait()` 时,会释放锁;当线程被唤醒并重新获得锁后,会从 `wait()` 后的第一条语句继续执行。
2. **线程唤醒:**
- 如果有多个线程都在等待同一个锁,则调用 `notify()` 只能唤醒其中一个线程,而 `notifyAll()` 则会唤醒所有等待的线程。
- 被唤醒的线程需要重新获取锁才能继续执行,这意味着它需要等待其他持有锁的线程释放锁。
#### 四、高级线程同步机制
Java除了提供传统的 `wait` 和 `notify` 方法外,还提供了更高级的线程同步机制,例如 `Lock` 接口及其实现类 `ReentrantLock`。
##### 1. Lock接口
`Lock` 接口提供了比使用 `synchronized` 更加灵活的锁定操作。通过 `Lock` 可以支持更复杂的锁定策略,比如可重入锁、读写锁等。`Lock` 接口中定义了 `lock()` 和 `unlock()` 方法来显式地控制锁的获取和释放,还提供了 `tryLock()` 方法尝试获取锁而不阻塞线程。
**示例代码:**
```java
Lock lock = new ReentrantLock();
lock.lock();
try {
// 执行临界区代码
} finally {
lock.unlock();
}
```
##### 2. Condition接口
`Condition` 接口是 `Lock` 接口的一部分,用于支持更细粒度的线程同步。与 `wait` 和 `notify` 不同,`Condition` 允许一个线程等待直到另一个线程发出信号。`Condition` 提供了 `await()` 和 `signal()` 方法来替代 `wait` 和 `notify`。
**示例代码:**
```java
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
lock.lock();
try {
while (!condition) {
condition.await();
}
// do something
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
lock.unlock();
}
```
##### 3. ReadWriteLock
`ReadWriteLock` 接口允许多个线程同时读取共享资源,但只允许一个线程写入资源。读取锁不会阻塞其他读取锁的请求,但会阻塞写入锁的请求。
**示例代码:**
```java
ReadWriteLock rwLock = new ReentrantReadWriteLock();
Lock readLock = rwLock.readLock();
Lock writeLock = rwLock.writeLock();
readLock.lock();
try {
// 读取操作
} finally {
readLock.unlock();
}
writeLock.lock();
try {
// 写入操作
} finally {
writeLock.unlock();
}
```
#### 五、总结
本文详细介绍了Java中线程同步的基本概念和技术,包括传统的 `wait` 和 `notify` 方法以及更高级的 `Lock` 接口和 `Condition` 接口。理解这些机制对于编写高效可靠的多线程应用程序至关重要。在实际应用中,应根据具体需求选择合适的同步机制,以提高程序的性能和可维护性。