Busy Wait
一个线程间相互通信的方法是使用线程间共享对象的一个变量进行通信。
public class MySignal{
protected boolean hasDataToProcess = false;
public synchronized boolean hasDataToProcess(){
return this.hasDataToProcess;
}
public synchronized void setHasDataToProcess(boolean hasData){
this.hasDataToProcess = hasData;
}
}
protected MySignal sharedSignal = ...
...
while(!sharedSignal.hasDataToProcess()){
//do nothing... busy waiting
}
线程B等待线程A将hasDataToProcess的值设置为true,这种方法cpu利用率不高
wait(), nofify(), notifyAll()
- 线程必须在同步块里面调用wait、notify、notifyall
- 调用wait时,线程释放锁;调用notify的时候,等待这个锁的线程里面的其中一个线程重新获取锁。
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();
}
}
}
Missed Signals
如果notify方法在wait方法之前被调用,那么wait方法调用以后可能导致永远不被唤醒。可以使用一个变量标记是否进行过唤醒操作。
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();
}
}
}
如果先调用了doNotify,那么在调用doWait的时候不会进行wait()操作
Spurious Wakeups
有时候线程会莫名其妙的被唤醒,即使没有调用notify、notifyAll。这是我们将if判断改成while判断,那么如果线程不是被doNotify唤醒,线程会重新进入wait状态。
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();
}
}
}
不要调用常量String或者全局变量的wait()
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();
}
}
}
当调用全局变量或者String常量的wait时,不同的线程其实获取的是同一把锁。
- 如果线程A调用notify,可能唤醒的是C或者D。这种情况类似于Spurious Wakeups,但是不会造成影响。
- 如果线程A想唤醒B,但是可能作用的是C或者D,那么唤醒B的信号就丢失了。
- 可以使用notifyAll,但是可能导致性能不好。