笔记:
1、通过共享对象通信
public class MySignal{
protected boolean hasDataToProcess = false;
public synchronized boolean hasDataToProcess(){
return this.hasDataToProcess;
}
public synchronized void setHasDataToProcess(boolean hasData){
this.hasDataToProcess = hasData;
}
}
通信的线程必须要获取指向同一个MySignal共享实例的引用。
2、忙等待
protected MySignal sharedSignal = ...
...
while(!sharedSignal.hasDataToProcess()){
//do nothing... busy waiting
}
问题:忙等待会浪费cpu,除非等待的时间很少,否则应该让等待的线程进入睡眠或者非运行状态。
3、wait(), notify() and notifyAll()
public class MonitorObject{
}
public class MyWaitNotify{
MonitorObject myMonitorObject = new MonitorObject();
public void doWait(){
synchronized(myMonitorObject){
try{
myMonitorObject.wait();
} catch(InterruptedException e){...}
}
}
public void doNotify(){
synchronized(myMonitorObject){
myMonitorObject.notify();
}
}
}
问题:如果doNotify()在doWait()之前调用,会丢失信号,等待的线程无法收到信号。应该保存可能丢失的信号。
4、Missed Signals
public class MyWaitNotify2{
MonitorObject myMonitorObject = new MonitorObject();
boolean wasSignalled = false;
public void doWait(){
synchronized(myMonitorObject){
if(!wasSignalled){
try{
myMonitorObject.wait();
} catch(InterruptedException e){...}
}
//clear signal and continue running.
wasSignalled = false;
}
}
public void doNotify(){
synchronized(myMonitorObject){
wasSignalled = true;
myMonitorObject.notify();
}
}
}
问题:如果由于未知的原因,导致等待的线程没有在其他线程调用notify()和notifyAll()的情况下醒来(假唤醒),即没有通过接收信号来唤醒,可能会导致严重的问题。
5、Spurious Wakeups
通过增加等待的线程退出等待的状态条件检查,即spin loop,解决假唤醒问题。
public class MyWaitNotify3{
MonitorObject myMonitorObject = new MonitorObject();
boolean wasSignalled = false;
public void doWait(){
synchronized(myMonitorObject){
while(!wasSignalled){
try{
myMonitorObject.wait();
} catch(InterruptedException e){...}
}
//clear signal and continue running.
wasSignalled = false;
}
}
public void doNotify(){
synchronized(myMonitorObject){
wasSignalled = true;
myMonitorObject.notify();
}
}
}
问题:doNotify()只能唤醒单个线程,即使将myMonitorObject.notify(); 改成myMonitorObject.notifyAll();也只能唤醒一个线程。因为wasSignalled只有一个,并且在被唤醒的线程执行完,会清空该标记。
6、Don’t call wait() on constant String’s or global objects
public class MyWaitNotify{
String myMonitorObject = "";
boolean wasSignalled = false;
public void doWait(){
synchronized(myMonitorObject){
while(!wasSignalled){
try{
myMonitorObject.wait();
} catch(InterruptedException e){...}
}
//clear signal and continue running.
wasSignalled = false;
}
}
public void doNotify(){
synchronized(myMonitorObject){
wasSignalled = true;
myMonitorObject.notify();
}
}
}
如果用常量字符串或者其他的常量或者全局对象作为监视器(monitor,管程),所有MyWaitNotify实例,会共用同一个监视器那个,可能导致使用notify可能没有通知到对应的等待线程,即唤醒了其他等待线程(相当于假唤醒)。虽然被错误唤醒的线程因为有wasSignalled保证,所以会重新进行等待,但是对于应该获取信号的线程而言,相当于信号丢失了。
所以,不要用常量作为锁监控对象。