對於初學者來說下面這個例子是一個非常常見的錯誤
/**
* 線程A 循環次後等待並放棄鎖讓線程B執行
*/
class ThreadA extends Thread{
//線程同步的公共數據區
Object oa=null;
ThreadA(Object o){
thisoa=o;
}
//線程A執行邏輯
public void run(){
//線程同步區域需要申請公共數據的鎖
synchronized(oa){
Systemoutprintln(ThreadA is running);
for(int i=;i<;i++){
Systemoutprintln( ThreadA value is +i);
if(i==){
try {
//當前線程等待
ThreadcurrentThread()wait();
} catch (InterruptedException e) {
eprintStackTrace();
}
}//if(i==)
}//for(int i)
}
}
}
/**
* 線程B等待線程A放棄鎖然後獲得鎖並執行完成後喚醒線程A
*/
class ThreadB extends Thread{
//線程同步的公共數據區
Object ob=null;
ThreadB(Object o){
thisob=o;
}
//線程B執行邏輯
public void run(){
//線程同步區域需要申請公共數據的鎖
synchronized(ob){
Systemoutprintln(ThreadB is running);
for(int i=;i<;i++){
Systemoutprintln( ThreadB value is +i);
}
//喚醒等待的線程
notify();
}
}
}
//測試
public class ThreadTest {
public static void main(String[] args){
Object lock=new Object(); //公共數據區
ThreadA threada=new ThreadA(lock);
ThreadB threadb=new ThreadB(lock);
threadastart(); //線程A執行
threadbstart(); //線程B執行
}
} 程序很簡單就是讓線程AB交替打印但是運行的時候會拋出兩個異常
Exception in thread Thread javalangIllegalMonitorStateException: current thread not owner
Exception in thread Thread javalangIllegalMonitorStateException: current thread not owner
問題就處在ThreadA中的ThreadcurrentThread()wait(); 和ThreadB中的notify();上
初學者理解wait()的時候都認為是將當前線程阻塞所以ThreadcurrentThread()wairt();視乎很有道理但是不知道大家有沒有發現在JDK類庫中wait()和notify()方法並不是Thread類的而是Object()中的我們仔細看看wait方法的JDK文檔
public final void wait() throws InterruptedException
Object (Java Platform SE )<! Generated by javadoc (build beta) on Thu Jan :: CST > <script type=text/javascript> function windowTitle() { if (locationhrefindexOf(isexternal=true) == ) { parentdocumenttitle="Object (Java Platform SE )"; } } </script>
<noscript></noscript>
在其他線程調用此對象的 notify() 方法或 notifyAll() 方法前導致當前線程等待 換句話說此方法的行為就好像它僅執行 wait() 調用一樣
當前線程必須擁有此 對象監視器 該線程發布對此監視器的所有權並等待 直到其他線程通過調用 notify 方法或 notifyAll 方法通知在此對象的監視器上等待的線程醒來 然後該線程將等到重新獲得對監視器的所有權後才能繼續執行
對於某一個參數的版本實現中斷和虛假喚醒是可能的而且此方法應始終在循環中使用
synchronized (obj) {
while (<condition does not hold>)
objwait();
// Perform action appropriate to condition
}
此方法只應由作為此對象監視器的所有者的線程來調用
拋出 IllegalMonitorStateException 如果當前線程不是此對象監視器的所有者
InterruptedException 如果在當前線程等待通知之前或者正在等待通知時任何線程中斷了當前線程在拋出此異常時當前線程的中斷狀態 被清除
看完JDK文檔以後很顯然只要把開始的程序中ThreadcurrentThread()wait();改成oawait() notify();改成 obnotify()就沒有問題了
也就是說只能通過同步塊obj來調用wait/notify方法 而不能通過想當然的線程調用這兩個方法至於為什麼是這樣我有一種想法大家可以一起討論一下
首先我們都知道JVM會給每一個對象都分配唯一的一把鎖這把鎖是在對象中的
然後當Thread線程獲得了這把鎖後應該是在對象中的鎖內記錄下當前占有自己的線程號並把自己設置為已被占用那麼當Thread需要放棄鎖的時候鎖對象會吧 Thread放入到鎖的等待隊列中 而這一切和Thread是沒有任何關系的自然也輪不到Thread對象來調用某個方法來改變另一個對象中的鎖(這一點也說不通我自己的鎖憑什麼讓你來改)
因此也就出現用改變公共數據區對象的鎖的方法是通過共數據區對象本省來調用和線程對象是沒有關系的
From:http://tw.wingwit.com/Article/program/Java/hx/201311/26192.html