共享堆記憶體變數屬於通訊的一種,今天要介紹的是使用object類中的wait和notify方法進行通訊,可以讓兩個執行緒共同地輪流做一件事
再看object類:object類是所有類的根父類,現在主要介紹wait方法和notify方法
void wait()讓當前執行緒進入等待狀態,直到別的執行緒將其喚醒(notify或者notifyall方法)。
void notify()隨機喚醒單個等待狀態的執行緒,被喚醒的執行緒接著執行之前沒完成的操作
void notifyall()將所有等待狀態的執行緒喚醒
我們接著做沒做完的練習,讓兩個執行緒輪流列印0-100
package chen_chapter_9;
public class waitnotifytest01
}class print implements runnable
if (!flag) catch (interruptedexception e)
} else
}} }
}out:
如果沒有else關鍵字,那麼執行緒從等待中被喚醒後會繼續執行進入等待前的操作,這樣會多列印出乙個數字100
分析:啟動執行緒t1,t2,可能t1先進入同步**塊,鎖住this物件,t2這時可能進入了run方法,當t1執行完列印
後設定flag=false,呼叫notify方法,但此時沒有等待的執行緒,這句話不起作用,因為在while語句中,t1還滿足
條件,沒有退出迴圈體,也沒有釋放鎖,再執行一次while迴圈體的語句,這時進入到if語句塊,設定flag=true,
被設定為wait,釋放鎖,t2開始執行,列印語句,將flag=false,呼叫notify喚醒t1,但t2沒釋放鎖,接著執行一
次while迴圈體,進入到if語句塊,設定flag=true,被設定成等待,釋放鎖,此時又該t1執行了,列印語句,設定
flag=false,如此迴圈,如果列印語句不在else語句塊中,當執行緒被喚醒後,會往下執行列印語句,導致結果不正確,
加入else語句塊中後,保證每次while迴圈體內只有乙個執行緒能執行
三個及多個執行緒之間的通訊,notify方法只能喚醒乙個正在等待的執行緒,如果多個執行緒在等待,再用這個方法,最後可能會出現所有執行緒都在等待的問題
notifyall方法將所有在等待的執行緒喚醒,
注意:
1 synchronized同步**塊鎖住的是哪個物件,就呼叫哪個鎖物件的wait方法和notify,notifyall方法,在這裡鎖住的是this,所以呼叫this.wait(),this.notify()
2 while和if
ifwait()方法在if條件判斷語句中,當被喚醒時,繼續往下執行
whilewait()方法在while迴圈語句中,當被喚醒時,要重新判斷while條件
3匿名內部類和區域性內部類在方法體中呼叫區域性變數時(包括區域性變數是物件的引用),要用final修飾,呼叫所在類的成員變數時,不需要final修飾,但是在main方法(靜態方法)中呼叫類的所在類的成員變數時,需要用static修飾
先說下為什麼呼叫區域性變數時要用final修飾?
方法中的類訪問同乙個方法中的區域性變數,本來應該是天經地義的,但是這樣會導致編譯程式上實現的困難,因為內部類物件的生命期會超過區域性變數的生命期
1區域性變數的生命期:當該方法被呼叫時,該方法中的區域性變數在棧中被建立,方法呼叫結束時,退棧,區域性變數消亡。
2 內部類生命期,跟其它類一樣,當建立乙個內部類物件後,引用為空時,這個內部類才消亡,不會因為它是在方法中定義的,當方法執行完畢它就消亡。
看到這裡出現端倪了,當內部類呼叫所在方法的區域性變數時,當所在的方法呼叫完成,區域性變數就會消亡,但是內部類物件不一定消亡了,(只要它的引用不為空就不會消亡),這時再呼叫該內部類物件的方法或屬性,如果用到該區域性變數,程式就出問題了,內部類物件訪問的屬性不存在,所以用final關鍵字,實際是將區域性變數複製乙份(final的作用保證基本資料型別值不變,引用型別引用不變,盡量保證了區域性變數和內部類的一致性),用做內部類的成員變數,當區域性變數消亡時,還可以用該屬性
還以上面為例,變成三個執行緒輪流列印0-99,將上面**稍微修改下就可以了
public class waitnotifytest01
}class print implements runnable
if (!flag) catch (interruptedexception e)
} else
}} }
}out:並不是嚴格的輪流列印,但是每一輪三個執行緒各打乙個
建立三個執行緒來演示多執行緒的通訊,
package chen_chapter_9;
public class waitnotifyalltest01 }}
};thread t2 = new thread("執行緒t2") }}
};thread t3 = new thread("執行緒t3") }}
};t1.start();
t2.start();
t3.start(); }}
class print2 catch (interruptedexception e)
}if (i < 100) else
this.flag = 2;
this.notifyall();
} }public void print2() catch (interruptedexception e)
}if (i < 100) else
this.flag = 3;
this.notifyall();
} }public void print3() catch (interruptedexception e)
}if (i < 100) else
this.flag = 1;
this.notifyall();
} }}out:三個輪流列印,且順序不變
分析:分析這塊**,設定flag=1,三個執行緒啟動時,t1,t2,t3,不知道誰會先拿到執行權,這三個執行緒執行一遍
之後,只有t1能執行語句,while語句中t2,t3進入等待狀態,當t1在同步**塊的內容執行完以後設定flag=2,
呼叫notifyall方法,喚醒t2,t3,這時三個執行緒又開始搶cpu控制權,但因為flag=2,這時只有t2執行緒能執
行,while語句中將t1,t3設定成等待狀態,t2執行完後將設定flag=3,呼叫notifyall方法,將t1,t3喚醒,三
個執行緒又開始搶cpu控制權,但是因為flag=3,所以只能t3執行緒執行,while語句中讓t1,t2變成等待狀態,t3執
行完同步**塊內容後,設定flag=1,又回到剛開始的迴圈,在這裡設定了個變數stopnum,是因為執行緒中的run
方法是死迴圈,要設定乙個停止條件
sleep wait notify的區別
sleep:睡眠一段時間不釋放鎖,醒來後繼續執行之前的任務,不釋放鎖
wait:等待後,釋放物件鎖,只能被別的執行緒喚醒notify,喚醒後接著執行之前未完的任務,注意在while和if語句塊中的區別
notify不釋放鎖
JAVA多執行緒之 執行緒通訊 Condition
執行緒的通訊 前面學習了用wait notify的方式進行執行緒通訊。今天學習一種更加強大的執行緒通訊方式condition.condition的強大之處就是可以為執行緒建立不同的condition。然後可以喚醒任意指定阻塞的執行緒。condition之所以能為乙個執行緒建立不同的condition...
多執行緒之間通訊
多執行緒之間通訊,其實就是多個執行緒在操作同乙個資源,但是操作的動作不同。需求 第乙個執行緒寫入 input 使用者,另乙個執行緒取讀取 out 使用者.實現讀乙個,寫乙個操作。共享資源源實體類 class res輸入執行緒資源 class intthrad extends thread overr...
多執行緒之間通訊
多執行緒之間通訊 就是多個執行緒在操作同乙個資源,但是操作的動作不同 現在需要實現,生產一台電機,銷售一台電機問題。實現 執行結果 資料發生錯亂,造成執行緒安全問題 解決執行緒安全問題 通過wait notify來解決。wait和sleep的區別 wait可以指定時間也可以不指定時間,sleep必須...