一、概念
存在这样的场景:
有两类线程,生产者线程跟消费者线程,生产者线程生产了产品会通知消费者线程进行消费,如果未消费的产品足够多了,则生产者线程会进行等待,待消费者线程消费了产品后再唤醒生产者线程。而消费者线程消费了产品会通知生产者线程进行生产,如果未消费的产品没有了,则消费者线程会进行等待,待生产者线程生产了产品后再唤醒消费者线程。这里用到的就是"等待/通知机制"。
等待/通知机制:
一个线程A调用了对象O的wait()方法进入等待状态,而另一个线程B调用了对象O的notify()/notifyAll()方法,线程A收到通知后退出等待队列,进入可运行状态,进而执行后续操作。上诉两个线程通过对象O来完成交互,而对象上的wait()方法和notify()/notifyAll()方法的关系就如同开关信号一样,用来完成等待方和通知方之间的交互工作。
二、相关方法
wait():使调用该方法的线程释放共享资源锁,然后从运行状态退出,进入等待队列,直到被再次唤醒。
wait(long):超时等待一段时间,这里的参数时间是毫秒,如果没有通知就超时返回。
wait(long, int):对于超时时间更细力度的控制,可以达到纳秒。
notify():唤醒等待队列中等待同一共享资源的"一个线程",并使该线程退出等待队列,进入可运行状态,具体唤醒哪一个线程取决于JVM虚拟机的实现。
notifyAll():唤醒等待队列中等待同一共享资源的"全部线程",并使这些线程退出等待队列,进入可运行状态,至于哪一个线程先执行任务取决于JVM虚拟机的实现。
三、注意事项
1.synchronized关键字可以将任何一个Object对象作为同步对象来看待,Java为每个Object都实现了等待/通知机制的相关方法,它们必须用在synchronized关键字同步的Object的临界区内。通过调用wait()方法可以使处于临界区内的线程进入等待状态,同时释放对象锁,而notify()/notifyAll()方法可以唤醒因调用wait()操作而处于等待状态中的线程,使其进入就绪状态,被唤醒的线程会重新试图获取对象锁,并继续执行wait()方法之后的代码。如果调用notify()/notifyAll()方法时没有处于阻塞状态中的线程,那么该命令会被忽略。
2.当方法wait()被执行后,对象锁会被自动释放,但执行完notify()/notifyAll()方法后,对象锁不会自动释放,必须执行完notify()/notifyAll()方法所在的synchronized代码块后,对象锁才释放。
3.当调用wait()方法使线程处于等待状态时,对该线程对象调用interrupt()方法会抛出InterrupedException异常。
四、使用
实现生产者消费者简单模型:
class BoundedBuffer {
final Object[] items = new Object[5];
int putptr, takeptr, count;
public synchronized void put(Object x) throws InterruptedException {
Log.d(TAG, "zwm, put method, thread: " + Thread.currentThread().getName() + " acquire lock");
while (count == items.length) {
Log.d(TAG, "zwm, buffer full, thread: " + Thread.currentThread().getName() + " wait");
wait();
}
items[putptr] = x;
Log.d(TAG, "zwm, thread: " + Thread.currentThread().getName() + " putptr: " + putptr + ", value: " + items[putptr]);
if (++putptr == items.length) putptr = 0;
++count;
Log.d(TAG, "zwm, put done, thread: " + Thread.currentThread().getName() + " notify other thread");
notifyAll();
Log.d(TAG, "zwm, put method, thread: " + Thread.currentThread().getName() + " release lock");
}
public synchronized Object take() throws InterruptedException {
Log.d(TAG, "zwm, take method, thread: " + Thread.currentThread().getName() + " acquire lock");
while (count == 0) {
Log.d(TAG, "zwm, buffer empty, thread: " + Thread.currentThread().getName() + " wait");
wait();
}
Object x = items[takeptr];
Log.d(TAG, "zwm, thread: " + Thread.currentThread().getName() + " takeptr: " + takeptr + ", value: " + items[takeptr]);
if (++takeptr == items.length) takeptr = 0;
--count;
Log.d(TAG, "zwm, take done, thread: " + Thread.currentThread().getName() + " notify other thread");
notifyAll();
Log.d(TAG, "zwm, take method, thread: " + Thread.currentThread().getName() + " release lock");
return x;
}
}
class PutThread extends Thread {
private BoundedBuffer buffer;
public PutThread(String name, BoundedBuffer buffer) {
setName(name);
this.buffer = buffer;
}
@Override
public void run() {
for(int i=0; i<5; i++) {
try {
buffer.put(String.valueOf(i));
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class TakeThread extends Thread {
private BoundedBuffer buffer;
public TakeThread(String name, BoundedBuffer buffer) {
setName(name);
this.buffer = buffer;
}
@Override
public void run() {
for(int i=0; i<5; i++) {
try {
buffer.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//测试代码
BoundedBuffer buffer = new BoundedBuffer();
PutThread putThread1 = new PutThread("putThread1", buffer);
putThread1.start();
TakeThread takeThread1 = new TakeThread("takeThread1", buffer);
takeThread1.start();
TakeThread takeThread2 = new TakeThread("takeThread2", buffer);
takeThread2.start();
//输出
2019-02-12 16:00:52.815 zwm, put method, thread: putThread1 acquire lock
2019-02-12 16:00:52.815 zwm, thread: putThread1 putptr: 0, value: 0
2019-02-12 16:00:52.815 zwm, put done, thread: putThread1 notify other thread
2019-02-12 16:00:52.815 zwm, put method, thread: putThread1 release lock
2019-02-12 16:00:52.819 zwm, take method, thread: takeThread1 acquire lock
2019-02-12 16:00:52.819 zwm, thread: takeThread1 takeptr: 0, value: 0
2019-02-12 16:00:52.820 zwm, take done, thread: takeThread1 notify other thread
2019-02-12 16:00:52.820 zwm, take method, thread: takeThread1 release lock
2019-02-12 16:00:52.820 zwm, take method, thread: takeThread1 acquire lock
2019-02-12 16:00:52.820 zwm, buffer empty, thread: takeThread1 wait //buffer空,takeThread1等待,释放锁
2019-02-12 16:00:52.823 zwm, take method, thread: takeThread2 acquire lock //takeThread2获取锁
2019-02-12 16:00:52.824 zwm, buffer empty, thread: takeThread2 wait //buffer空,takeThread2等待,释放锁
2019-02-12 16:00:53.816 zwm, put method, thread: putThread1 acquire lock
2019-02-12 16:00:53.817 zwm, thread: putThread1 putptr: 1, value: 1
2019-02-12 16:00:53.818 zwm, put done, thread: putThread1 notify other thread
2019-02-12 16:00:53.818 zwm, put method, thread: putThread1 release lock
2019-02-12 16:00:53.819 zwm, thread: takeThread2 takeptr: 1, value: 1 //takeThread2被唤醒,获取锁
2019-02-12 16:00:53.819 zwm, take done, thread: takeThread2 notify other thread
2019-02-12 16:00:53.820 zwm, take method, thread: takeThread2 release lock
2019-02-12 16:00:53.820 zwm, buffer empty, thread: takeThread1 wait //takeThread1被唤醒,获取锁;buffer空,takeThread1等待,释放锁
2019-02-12 16:00:53.821 zwm, take method, thread: takeThread2 acquire lock //takeThread2获取锁
2019-02-12 16:00:53.822 zwm, buffer empty, thread: takeThread2 wait //buffer空,takeThread2等待,释放锁
2019-02-12 16:00:54.819 zwm, put method, thread: putThread1 acquire lock
2019-02-12 16:00:54.820 zwm, thread: putThread1 putptr: 2, value: 2
2019-02-12 16:00:54.820 zwm, put done, thread: putThread1 notify other thread
2019-02-12 16:00:54.821 zwm, put method, thread: putThread1 release lock
2019-02-12 16:00:54.822 zwm, thread: takeThread1 takeptr: 2, value: 2
2019-02-12 16:00:54.822 zwm, take done, thread: takeThread1 notify other thread
2019-02-12 16:00:54.823 zwm, take method, thread: takeThread1 release lock
2019-02-12 16:00:54.823 zwm, take method, thread: takeThread1 acquire lock
2019-02-12 16:00:54.823 zwm, buffer empty, thread: takeThread1 wait
2019-02-12 16:00:54.824 zwm, buffer empty, thread: takeThread2 wait
2019-02-12 16:00:55.822 zwm, put method, thread: putThread1 acquire lock
2019-02-12 16:00:55.822 zwm, thread: putThread1 putptr: 3, value: 3
2019-02-12 16:00:55.822 zwm, put done, thread: putThread1 notify other thread
2019-02-12 16:00:55.822 zwm, put method, thread: putThread1 release lock
2019-02-12 16:00:55.823 zwm, thread: takeThread2 takeptr: 3, value: 3
2019-02-12 16:00:55.823 zwm, take done, thread: takeThread2 notify other thread
2019-02-12 16:00:55.823 zwm, take method, thread: takeThread2 release lock
2019-02-12 16:00:55.823 zwm, take method, thread: takeThread2 acquire lock
2019-02-12 16:00:55.823 zwm, buffer empty, thread: takeThread2 wait
2019-02-12 16:00:55.823 zwm, buffer empty, thread: takeThread1 wait
2019-02-12 16:00:56.823 zwm, put method, thread: putThread1 acquire lock
2019-02-12 16:00:56.823 zwm, thread: putThread1 putptr: 4, value: 4
2019-02-12 16:00:56.824 zwm, put done, thread: putThread1 notify other thread
2019-02-12 16:00:56.824 zwm, put method, thread: putThread1 release lock
2019-02-12 16:00:56.824 zwm, thread: takeThread1 takeptr: 4, value: 4
2019-02-12 16:00:56.824 zwm, take done, thread: takeThread1 notify other thread
2019-02-12 16:00:56.824 zwm, take method, thread: takeThread1 release lock
2019-02-12 16:00:56.824 zwm, take method, thread: takeThread1 acquire lock
2019-02-12 16:00:56.825 zwm, buffer empty, thread: takeThread1 wait
2019-02-12 16:00:56.825 zwm, buffer empty, thread: takeThread2 wait