notify和 wait 详细介绍notify和wait的详细介绍如下:
一、notify
1. 基本概念
notify是Java中的一个方法,属于Object类,用于唤醒正在等待该对象监视器的单个线程。当某个线程调用了某个对象的wait()方法后,该线程会进入等待状态并释放该对象的锁,直到其他线程调用此对象的notify()方法或者notifyAll()方法,或者等待超时,该线程才会重新获得锁并进入就绪状态。
2. 使用场景
notify方法通常用于线程间的通信,特别是在生产者-消费者模型中。生产者线程在生产出数据后,通过调用notify方法唤醒等待在该对象上的消费者线程,使其能够继续执行并处理数据。
3. 注意事项
- notify方法必须在同步代码块或同步方法中调用,且必须持有调用该方法的对象的锁。
- notify方法一次只能唤醒一个等待线程,如果有多个线程在等待,那么唤醒哪个线程是不确定的。如果需要唤醒所有等待线程,应使用notifyAll方法。
- 被唤醒的线程不会立即执行,而是会进入就绪状态,等待CPU的调度。
二、wait
1. 基本概念
wait也是Java中的一个方法,同样属于Object类,用于使当前线程等待,直到其他线程调用此对象的notify()方法或notifyAll()方法,或者等待的时间超过指定的时间长度。调用wait方法会释放当前线程持有的对象的锁,并导致当前线程进入等待状态。
2. 使用场景
wait方法通常用于线程间的同步控制,特别是在需要等待某个条件成立的场景中。例如,在消费者线程中,如果队列为空,消费者线程可以通过调用wait方法进入等待状态,直到生产者线程生产了数据并调用notify或notifyAll方法唤醒它。
3. 注意事项
- wait方法必须在同步代码块或同步方法中调用,且必须持有调用该方法的对象的锁。
- 调用wait方法会释放当前线程持有的锁,这是wait和sleep方法的一个重要区别。
- wait方法可以通过传递参数来指定等待的时间,如果时间到了还没有被唤醒,线程会自动醒来并重新尝试获取锁。
- 在等待过程中,如果线程被中断,那么wait方法会抛出InterruptedException异常。
综上所述,notify和wait是Java中用于线程间通信和同步控制的重要方法,它们通过对象的监视器锁来实现线程间的协调与配合。在使用时需要注意它们的使用场景和注意事项,以确保程序的正确性和高效性。以下通过示例来详细讲解Java中的notify和wait方法:
示例背景
假设我们有一个简单的生产者-消费者问题,其中生产者线程负责生成数据并将其放入缓冲区,消费者线程从缓冲区中取出数据进行处理。为了同步生产者和消费者的操作,我们可以使用wait和notify方法。
示例代码
public class ProducerConsumerExample {
private List<Integer> buffer = new ArrayList<>();
private int capacity = 5; // 缓冲区容量
// 生产者方法
public synchronized void produce(int value) throws InterruptedException {
while (buffer.size() == capacity) {
wait(); // 缓冲区满时,生产者线程等待
}
buffer.add(value);
System.out.println("Produced: " + value);
notify(); // 唤醒在缓冲区上等待的消费者线程
}
// 消费者方法
public synchronized void consume() throws InterruptedException {
while (buffer.isEmpty()) {
wait(); // 缓冲区空时,消费者线程等待
}
int value = buffer.remove(0);
System.out.println("Consumed: " + value);
notify(); // 唤醒在缓冲区上等待的生产者线程(尽管在此示例中可能不是必需的,但为了演示目的包含)
}
public static void main(String[] args) {
ProducerConsumerExample pc = new ProducerConsumerExample();
// 生产者线程
Thread producer = new Thread(() -> {
try {
for (int i = 0; i < 10; i++) {
pc.produce(i);
Thread.sleep(100); // 模拟生产耗时
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
// 消费者线程
Thread consumer = new Thread(() -> {
try {
while (true) {
pc.consume();
Thread.sleep(200); // 模拟消费耗时
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
producer.start();
consumer.start();
}
}
讲解
-
synchronized关键字:
produce和consume方法都被声明为synchronized,这意味着在同一时刻,只有一个线程可以执行这些方法中的任何一个。这保证了线程安全,防止多个线程同时访问缓冲区导致数据不一致。
-
wait方法:
- 当缓冲区满时(
buffer.size() == capacity),生产者线程调用wait()方法进入等待状态,并释放缓冲区对象的锁。此时,生产者线程暂停执行,直到其他线程调用缓冲区对象的notify()或notifyAll()方法。 - 类似地,当缓冲区空时(
buffer.isEmpty()),消费者线程也会调用wait()方法等待。
- 当缓冲区满时(
-
notify方法:
- 当生产者线程向缓冲区中添加数据后,它会调用
notify()方法来唤醒可能正在缓冲区对象上等待的消费者线程。注意,notify()方法只会唤醒一个等待线程,如果有多个线程在等待,那么唤醒哪个线程是不确定的。 - 在这个示例中,消费者线程在消费数据后也调用了
notify()方法,尽管这在实际的生产者-消费者模型中可能不是必需的,因为消费者通常不需要主动唤醒生产者(除非有特定的同步需求)。但这里为了演示notify()方法的使用而包含。
- 当生产者线程向缓冲区中添加数据后,它会调用
-
唤醒与竞争锁:
- 被
notify()唤醒的线程不会立即执行,而是会进入就绪状态,等待CPU的调度。当该线程获得CPU时间片时,它会尝试重新获取之前释放的锁。如果此时锁仍然被其他线程持有,那么该线程将再次进入阻塞状态,直到锁被释放。 - 一旦线程成功获取锁,它将从
wait()方法之后的位置继续执行。
- 被
通过这个示例,我们可以看到notify和wait方法在多线程编程中的重要作用,它们是实现线程间通信和同步控制的关键机制。
欢迎访问我的公众号或博客(点击查看头像信息)