1. 概述
wait()、notify()、notifyAll() 适应于生产者和消费者场景,结合synchronized使用。
2. 实现原理
wait()
wait()
方法为Object
对象的方法,执行该方法之前, 必须先获得锁。执行该方法后,会释放锁,使线程等待,进入阻塞队列。
notify()
notify()
默认的唤醒策略是:先进入wait()
的线程先被唤醒。
notifyAll()
notifyAll()
唤醒所有的等待线程,默认的唤醒策略是:LIFO
(后进先出)。
3. 集合WaitSet和EntrySet
JVM会为每一个使用内部锁(synchronized)的对象维护着两个集合:WaitSet
和EntrySet (抢锁机制)
。
- Wait Set:如果线程A调用了
wait()
方法,那么线程A释放对象锁后,进入到阻塞队列,此时处于Wating
状态。(存储被阻塞的线程) - Entry Set:如果线程A唤醒后并且持有了对象锁,此时,如果还有其他线程也想获得该对象所的话,它只能进入就绪队列等待CPU调度,并且处于线程的
Blocked
状态。(存储将要获得锁的线程)
4. 集合中的线程在什么条件下可以转变为Runnable状态?
- 对于
EntrySet
中的线程,当对象锁被释放的时候,JVM会唤醒处于Entry Set
中的某一个线程,这个线程的状态就从Blocked
转变为Runnable
状态。 - 对于
WaitSet
中的线程,当对象的notify()
方法调用时,JVM会唤醒处于WaitSet中的某一个线程,这个线程的状态就从Wating状态转变为Runnable。或者当notifyAll()
方法被调用时,WaitSet中的全部线程会转变为Runnable状态,所有WaitSet中被唤醒的线程会被转移到EntrySet中。
5. 为什么wait()方法要在循环中调用?
首先,没有规定说必须要放在while循环,至于放在哪是根据要实现的逻辑决定的。
一般来说,wait()肯定是在触发某个条件时调用的,不是if
就是while
。放在while里面是防止处于Wating的线程被唤醒后,while里面的条件并没有满足(也有可能当时满足了,但是由于别的线程操作之后,又不满足了),这时就需要再次调用wait()进入阻塞队列,等待下次被唤醒。
6. 为什么wait()、notify()和notifyAll()要与synchronized一起使用?
有synchronized的地方不一定有wait()和notify(),但有wait()和notify()的地方必有synchronized,这是因为wait()和notify()不属于线程类,而是每一个对象都具有的方法。而且,这两个方法都和对象锁有关,有锁的地方,必须有synchronized。
7. 为什么wait()、notify()和notifyAll()这些方法不在thread类里面?
简单的说,由于wait()、notify()和notifyAll()都是锁级别的操作,所以把他们定义在Object类中,因为锁属于对象。
8. 使用示例
public class ProducerConsumer {
public static void main(String args[]) {
Queue<Integer> queue = new LinkedList<>();
Thread producer = new Producer(queue);
Thread consumerA = new ConsumerA(queue);
Thread consumerB = new ConsumerB(queue);
producer.start();
consumerA.start();
consumerB.start();
}
}
/**
* Producer
*/
class Producer extends Thread {
private Queue<Integer> queue;
public Producer(Queue<Integer> queue) {
this.queue = queue;
}
@Override
public void run() {
while (true) {
synchronized (queue) {
while (!queue.isEmpty()) {
try {
queue.wait();
} catch (Exception ex) {
ex.printStackTrace();
}
}
Random random = new Random();
int i = random.nextInt();
System.out.println("生产数据: " + i);
queue.add(i);
// queue.notify();
queue.notifyAll();
}
}
}
}
/**
* Consumer
*/
class ConsumerA extends Thread {
private Queue<Integer> queue;
public ConsumerA(Queue<Integer> queue) {
this.queue = queue;
}
@Override
public void run() {
while (true) {
synchronized (queue) {
while (queue.isEmpty()) {
try {
queue.wait();
} catch (Exception ex) {
ex.printStackTrace();
}
}
System.out.println("消费数据A: " + queue.remove());
queue.notifyAll();
}
}
}
}
class ConsumerB extends Thread {
private Queue<Integer> queue;
public ConsumerB(Queue<Integer> queue) {
this.queue = queue;
}
@Override
public void run() {
while (true) {
synchronized (queue) {
while (queue.isEmpty()) {
try {
queue.wait();
} catch (Exception ex) {
ex.printStackTrace();
}
}
System.out.println("消费数据B: " + queue.remove());
queue.notifyAll();
}
}
}
}
网友评论