线程间的协作
一 等待和通知 介绍
等待/通知机制是指一个线程A调用了对象obejct的wait()方法进入等待状态,而另一个线程B调用了对象obejct的notify()或者notifyAll()方法,线程A收到通知后从对象obejct的wait()方法返回,进而执行后续操作。上述两个线程通过对象obejct来完成交互,而对象上的wait()和notify/notifyAll()的关系就如同开关信号一样,用来完成等待方和通知方之间的交互工作
notify():
通知一个在对象上等待的线程,使其从wait()方法返回,而返回的前提是该线程获取到了对象的锁。哪个线程能得到通知是随机的,不能指定。
notifyAll():
通知所有等待在该对象上的线程,这些线程会去竞争对象锁,得到锁的某一个线程可以继续执行wait()后的逻辑。
wait():
调用该方法的线程进入 WAITING状态,只有等待另外线程的通知或被中断才会返回。需要注意,调用wait()方法后,会释放对象的锁。
wait(long):
超时等待一段时间,这里的参数时间是毫秒,也就是等待长达n毫秒,如果没有通知就超时返回。
wait (long,int):
对于超时时间更细粒度的控制,可以达到纳秒。
二 等待/通知机制使用的标准范式
等待方
1 获取对象的琐
2 循环里判断条件是否满足,不满足则调用wait()方法 对象.wait();
3 满足条件则往下执行业务逻辑
synchronized (对象) { while (条件不满足) { 对象.wait(); } 对应逻辑处理 }
通知方
1 获取到对象的琐
2 改变条件
3 通知所有等待在对象上的线程(对象.notify() 或者对象.notifyAll() 放在同步代码块最后一行)
synchronized (对象) { 改变条件 对象.notifyAll(); }
在调用wait()、notify()和notifyAll()方法之前,线程必须要获得该对象的对象锁,即只能在同步方法或同步块中调用wait()方法、notify()和notifyAll()方法。
调用wait()方法后,当前线程释放锁, 执行notify()和notifyAll()方法的线程退出synchronized代码块的时候,假设是执行的notifyAll(),会唤醒所有处于等待的线程,这些线程会去竞争对象锁。如果其中一个线程A获得了该对象锁,线程A就会继续往下执行,其余被唤醒的线程处于阻塞状态。在线程A退出synchronized代码块释放锁后,其余已经被唤醒的处于阻塞状态的线程将会继续竞争该锁,一直进行下去,直到所有被唤醒的线程都执行完毕。
三 notify()和notifyAll()应该用谁
尽可能用notifyAll(),谨慎使用notify(),因为notify()只会唤醒一个线程,我们无法确保被唤醒的这个线程一定就是我们需要唤醒的线程。容易造成信号丢失情况。
四 线程什么时候会释放琐?
调用yield() 、sleep()、wait()、notify()等方法对锁有何影响?
答:yield() 、sleep()被调用后,都不会释放当前线程所持有的锁。只有wait()会释放琐
调用wait()方法后,会释放当前线程持有的锁,然后发送方才有可能获取到该琐,做自己业务。
而且当前线程被唤醒后(被notify后),会重新去竞争锁,得到锁到后才会执行wait()方法后面的代码。
调用notify()系列方法后,对锁无影响,线程只有在synchronized同步代码执行完后才会自然而然的释放锁,
所以notify()系列方法一般都是synchronized同步代码的最后一行。
五 为获取对象琐阻塞的线程和中止的线程
一个对象有一把锁和两个队列,对于所有无法获取到锁的线程都将被阻塞在阻塞队列上,而对于获取到锁以后,于运行过程中由于缺少某些条件而不得不中止程序的线程将被阻塞在条件队列上并让出CPU。