wait()
和notify()
方法是用于处理多线程同步的关键方法之一。它们通常用于协调多个线程对共享资源的访问和修改。
有一个共享的缓冲区,生产者负责往缓冲区中添加数据,消费者负责从缓冲区中取出数据。当缓冲区已满时,生产者需要等待消费者从缓冲区中取出一些数据后再继续添加;当缓冲区为空时,消费者需要等待生产者往缓冲区中添加一些数据后再继续取出。
定义一个名为SharedBuffer
的类,它代表共享的缓冲区。这个类有两个属性:一个是整型的bufferSize
,表示缓冲区的大小;另一个是队列queue
,用于存储数据。它还有两个方法:一个是put()
,用于生产者往缓冲区中添加数据;另一个是get()
,用于消费者从缓冲区中取出数据。
import java.util.LinkedList;
import java.util.Queue;
public class SharedBuffer {
private int bufferSize;
private Queue<Integer> queue;
public SharedBuffer(int bufferSize) {
this.bufferSize = bufferSize;
this.queue = new LinkedList<>();
}
public synchronized void put(int data) throws InterruptedException {
while (queue.size() == bufferSize) {
// 当缓冲区已满时,调用wait()方法使当前线程等待,并释放对象锁
wait();
}
queue.offer(data);
// 唤醒正在等待从缓冲区中取数据的消费者线程
notifyAll();
}
public synchronized Integer get() throws InterruptedException {
while (queue.isEmpty()) {
// 当缓冲区为空时,调用wait()方法使当前线程等待,并释放对象锁
wait();
}
Integer data = queue.poll();
// 唤醒正在等待往缓冲区中添加数据的生产者线程
notifyAll();
return data;
}
}
当生产者调用put()
方法往缓冲区中添加数据时,如果缓冲区已满,它会调用wait()
方法使当前线程等待,并释放对象锁。当消费者从缓冲区中取出一些数据后,它会调用notifyAll()
方法唤醒正在等待的生产者线程。同样地,当消费者调用get()
方法从缓冲区中取出数据时,如果缓冲区为空,它也会调用wait()
方法使当前线程等待,并在生产者往缓冲区中添加一些数据后通过调用notifyAll()
方法唤醒消费者线程。这样,生产者和消费者就可以在多线程环境下安全地共享缓冲区了。需要注意的是,在这个例子中,我们使用了synchronized
关键字来确保同一时刻只有一个线程可以执行put()
或get()
方法。这是因为这两个方法都需要访问和修改共享资源(即缓冲区),所以需要进行同步处理以避免数据竞争和不一致的问题。
Wait() 方法使当前线程等待,并释放对象锁,直到其他线程调用该对象的notify()或notifyAll()方法唤醒该线程。它通常用于让当前线程等待,直到共享资源可用或满足某些条件。使用wait()方法时,需要先获取对象锁,否则会抛出IllegalMonitorStateException异常。
wait等待结束条件:
Notify() 方法唤醒在该对象上等待的单个线程。它通常用于通知等待该对象的单个线程,共享资源已经可用或满足某些条件。使用notify()方法时,需要先获取对象锁,否则会抛出IllegalMonitorStateException异常。
notify注意事项:
Notify All() 方法唤醒在该对象上等待的所有线程。它通常用于通知等待该对象的所有线程,共享资源已经可用或满足某些条件。使用notifyAll()方法时,需要先获取对象锁,否则会抛出IllegalMonitorStateException异常。
notifyAll注意事项:
虽然是同时唤醒 多个线程, 但是这 多个线程需要竞争锁,所以并不是同时执行,而仍然是先获取锁的先执行。
public class SharedResource {
private int count = 0;
private Object lock = new Object();
public void increment() {
synchronized (lock) {
count++;
System.out.println("Count is: " + count);
lock.notify(); // 唤醒一个等待线程
}
}
public void decrement() throws InterruptedException {
synchronized (lock) {
count--;
System.out.println("Count is: " + count);
lock.wait(); // 当前线程等待,直到其他线程调用notify或notifyAll唤醒
}
}
}
有一个共享资源类SharedResource
,其中有一个计数器count
和一个锁对象lock
。increment()
方法会增加计数器的值,并使用notify()
方法唤醒一个等待线程。decrement()
方法会减少计数器的值,并使用wait()
方法使当前线程等待,直到其他线程调用notify()
或notifyAll()
方法唤醒。
(1)wait wait()方法是 Object类 提供的实例方法,可以使线程进入等待状态,直到其他线程调用了该对象的notify()或notifyAll()方法。通常被用于线程间通信,如生产者-消费者模式中(后续介绍),消费者需要等待生产者通知有新数据可取。当线程调用 wait() 方法时,它会释放占据的锁,并且线程的状态为WAITING,直到notify()或notifyAll()方法被调用。
(2)join join() 方法是 Thread类 提供的方法静态方法,用于等待被调用线程执行完毕。在主线程中调用了子线程的join() 方法后,主线程会进入 WAITING 状态,直到子线程执行完毕才会继续执行。可以用来保证多个线程按照指定顺序执行。
(3)sleep sleep()方法也是 Thread类 提供的实例方法,它可以使当前线程暂停执行一段时间。当线程调用 sleep() 方法时,它不会释放锁,线程的状态为 TIMED_WAITING 。通常被用于控制程序执行的速度或时间,或常常在循环内部以等待某些条件的发生。
wait() 方法是用于线程间的通信,join() 方法是用于等待其他线程执行完毕,sleep() 方法是用于暂停当前线程的执行。在使用上, wait 需要搭配 synchronized 使用,sleep 和 join 则不需要。