漫畫:Object類很大,你忍一下(完結(jié)篇)
時間:2021-08-19 15:34:16
手機看文章
掃描二維碼
隨時隨地手機看文章
[導(dǎo)讀]在上一篇當中,我們提及了Java語言O(shè)bject類的九大方法,并重點講解了其中的getClass(),finalize(),toString(),equals(),hashcode()。沒看過的小伙伴,可以點擊閱讀上一篇:漫畫:Object類很大,你忍一下這一次,我們來重點講解w...
在上一篇當中,我們提及了Java語言O(shè)bject類的九大方法,并重點講解了其中的getClass(),finalize(),toString(),equals(),hashcode()。
沒看過的小伙伴,可以點擊閱讀上一篇:漫畫:Object類很大,你忍一下
這一次,我們來重點講解 wait(),notify(),notifyAll() 這三大方法。
含參數(shù)的wait()方法調(diào)用以后,線程可以在等待時間結(jié)束后被喚醒;無參的wait()方法調(diào)用后,則必須等待持有該對象監(jiān)視器的線程主動調(diào)用notify()或notifyAll()方法后才能被喚醒。
兩者之間的區(qū)別,在于notify()方法喚醒在此對象監(jiān)視器上等待的單個線程,而notifyAll()方法則喚醒在此對象監(jiān)視器上等待的所有線程。
?與wait方法一樣,執(zhí)行notify方法的線程也必須提前獲取鎖。需要注意的是,被notify方法喚醒的線程并不會立即執(zhí)行,因為要等調(diào)用notify方法的線程釋放鎖之后才會獲取到鎖。
有的Java虛擬機(VM)會選擇最先調(diào)用wait方法的線程,也有的則會隨機選擇一個線程。盡管notify方法的處理速度比notifyAll方法更快,但使用notifyAll方法更為穩(wěn)妥。
notify與notifyAll的方法聲明如下:
有一家公司開始想要招聘程序員,于是面試了幾名候選人:
但公司錄用程序員的時間是不確定的,需要綜合考慮,無法給出及時反饋。于是告訴他們回去等通知。
經(jīng)過事后的比較和研究,面試官覺得大黃是招聘的最佳人選,于是讓HR小姐姐通知大黃,也就是“喚醒”了大黃:
(1)wait()、notify()和notifyAll()必須在synchronized修飾的方法或代碼塊中使用。(2)在while循環(huán)里而不是if語句下使用wait(),確保在線程睡眠前后都檢查wait()觸發(fā)的條件(防止虛假喚醒)。(3)wait()方法必須在多線程共享的對象上調(diào)用。?
生產(chǎn)者/消費者模型能解決絕大多數(shù)并發(fā)問題,通過平衡生產(chǎn)線程和消費線程的工作能力,來提高程序的整體處理數(shù)據(jù)的速度。
生產(chǎn)者線程和消費者線程的處理速度差異,會引起消費者想要獲取數(shù)據(jù)時,數(shù)據(jù)還沒生成或者生產(chǎn)者想要交付數(shù)據(jù),卻沒有消費者接收的問題。對于這樣的問題,生產(chǎn)者/消費者模型可以消除這個差異。
首先,按照注意事項(1)和(2)的要求,定義一個生產(chǎn)者,往隊列里添加元素:
接下來,再定義一個與之類似的消費者類,除了從隊列里移除元素的邏輯之外,整體代碼大同小異:
最后編寫符合注意事項(3)的測試代碼:
最終的查看運行結(jié)果如下:?
沒看過的小伙伴,可以點擊閱讀上一篇:漫畫:Object類很大,你忍一下
這一次,我們來重點講解 wait(),notify(),notifyAll() 這三大方法。
//?執(zhí)行這個方法后,持有此對象監(jiān)視器的線程會進入等待隊列,同時釋放鎖
//?如果不在synchronized修飾的方法或代碼塊里調(diào)用,則會拋出IllegalMonitorStateException?異常
//?如果當前線程在等待時被中斷,則拋出InterruptedException異常
public?final?void?wait()?throws?InterruptedException?{
????wait(0);
}
//?timeout是線程等待時間,時間結(jié)束則自動喚醒,單位ms
//?Java默認的實現(xiàn)方式,native實現(xiàn)
public?final?native?void?wait(long?timeout)?throws?InterruptedException;
//?nanos是更精確的線程等待時間,單位ns(1?ms?=?1,000,000?ns)
//?Java默認的實現(xiàn)方式
public?final?void?wait(long?timeout,?int?nanos)?throws?InterruptedException?{
????if?(timeout?0)?{
????????throw?new?IllegalArgumentException("timeout?value?is?negative");
????}
????if?(nanos?0?||?nanos?>?999999)?{
????????throw?new?IllegalArgumentException(
????????????????"nanosecond?timeout?value?out?of?range");
????}
????if?(nanos?>?0)?{
????????timeout ;
????}
????wait(timeout);
}
含參數(shù)的wait()方法調(diào)用以后,線程可以在等待時間結(jié)束后被喚醒;無參的wait()方法調(diào)用后,則必須等待持有該對象監(jiān)視器的線程主動調(diào)用notify()或notifyAll()方法后才能被喚醒。
兩者之間的區(qū)別,在于notify()方法喚醒在此對象監(jiān)視器上等待的單個線程,而notifyAll()方法則喚醒在此對象監(jiān)視器上等待的所有線程。
?與wait方法一樣,執(zhí)行notify方法的線程也必須提前獲取鎖。需要注意的是,被notify方法喚醒的線程并不會立即執(zhí)行,因為要等調(diào)用notify方法的線程釋放鎖之后才會獲取到鎖。
有的Java虛擬機(VM)會選擇最先調(diào)用wait方法的線程,也有的則會隨機選擇一個線程。盡管notify方法的處理速度比notifyAll方法更快,但使用notifyAll方法更為穩(wěn)妥。
notify與notifyAll的方法聲明如下:
public?final?native?void?notify();
public?final?native?void?notifyAll();
有一家公司開始想要招聘程序員,于是面試了幾名候選人:
但公司錄用程序員的時間是不確定的,需要綜合考慮,無法給出及時反饋。于是告訴他們回去等通知。
經(jīng)過事后的比較和研究,面試官覺得大黃是招聘的最佳人選,于是讓HR小姐姐通知大黃,也就是“喚醒”了大黃:
(1)wait()、notify()和notifyAll()必須在synchronized修飾的方法或代碼塊中使用。(2)在while循環(huán)里而不是if語句下使用wait(),確保在線程睡眠前后都檢查wait()觸發(fā)的條件(防止虛假喚醒)。(3)wait()方法必須在多線程共享的對象上調(diào)用。?
生產(chǎn)者/消費者模型能解決絕大多數(shù)并發(fā)問題,通過平衡生產(chǎn)線程和消費線程的工作能力,來提高程序的整體處理數(shù)據(jù)的速度。
生產(chǎn)者線程和消費者線程的處理速度差異,會引起消費者想要獲取數(shù)據(jù)時,數(shù)據(jù)還沒生成或者生產(chǎn)者想要交付數(shù)據(jù),卻沒有消費者接收的問題。對于這樣的問題,生產(chǎn)者/消費者模型可以消除這個差異。
首先,按照注意事項(1)和(2)的要求,定義一個生產(chǎn)者,往隊列里添加元素:
//?生產(chǎn)者,有詳細的注釋
public?class?Producer?implements?Runnable{
????private?Queue?queue;
????private?int?maxSize;
????public?Producer(Queue?queue,?int?maxSize) {
????????this.queue?=?queue;
????????this.maxSize?=?maxSize;
????}
????@Override
????public?void?run()?{
????????//?這里為了方便演示做了一個死循環(huán),現(xiàn)實開發(fā)中不要這樣搞
????????while?(true){
????????????//(1)wait()、notify()和notifyAll()必須在synchronized修飾的方法或代碼塊中使用
????????????synchronized?(queue){
//(2)在while循環(huán)里而不是if語句下使用wait(),確保在線程睡眠前后都檢查wait()觸發(fā)的條件(防止虛假喚醒)
????????????????while?(queue.size()?==?maxSize){
????????????????????try{
????????????????????????System.out.println("Queue?is?Full");
//?生產(chǎn)者線程進入等待狀態(tài),在此對象監(jiān)視器上等待的所有線程(其實只有那個消費者線程)開始爭奪鎖
????????????????????????queue.wait();
????????????????????}catch?(InterruptedException?ie){
????????????????????????ie.printStackTrace();
????????????????????}
????????????????}
????????????????Random?random?=?new?Random();
????????????????int?i?=?random.nextInt();
????????????????System.out.println("Produce?"? ?i);
????????????????queue.add(i);
//?喚醒這個Queue對象的等待池中的所有線程(其實只有那個消費者線程),等待獲取對象監(jiān)視器
????????????????queue.notifyAll();
????????????}
????????}
????}
}
接下來,再定義一個與之類似的消費者類,除了從隊列里移除元素的邏輯之外,整體代碼大同小異:
//?消費者類
public?class?Consumer?implements?Runnable{
????private?Queue?queue;
????private?int?maxSize;
????public?Consumer(Queue?queue,?int?maxSize) {
????????this.queue?=?queue;
????????this.maxSize?=?maxSize;
????}
????@Override
????public?void?run()?{
????????while?(true){
????????????synchronized?(queue){
????????????????while?(queue.isEmpty()){
????????????????????System.out.println("Queue?is?Empty");
????????????????????try{
????????????????????????queue.wait();
????????????????????}catch?(InterruptedException?ie){
????????????????????????ie.printStackTrace();
????????????????????}
????????????????}
????????????????int?v?=?queue.remove();
????????????????System.out.println("Consume?"? ?v);
????????????????queue.notifyAll();
????????????}
????????}
????}
}
最后編寫符合注意事項(3)的測試代碼:
public?void?test(){
????//(3)wait()方法必須在多線程共享的對象上調(diào)用
????//?這個隊列就是給消費者、生產(chǎn)者兩個線程共享的對象
????Queue?queue?=?new?LinkedList<>();
????int?maxSize?=?5;
????Producer?p?=?new?Producer(queue,?maxSize);
????Consumer?c?=?new?Consumer(queue,?maxSize);
????Thread?pT?=?new?Thread(p);
????Thread?pC?=?new?Thread(c);
????//?生產(chǎn)者線程啟動,獲取鎖
????pT.start();
????//?消費者線程啟動
????pC.start();
}
最終的查看運行結(jié)果如下:?
Produce?1604006010
Produce?1312202442
Produce?-1478853208
Produce?1460408111
Produce?1802825495
Queue?is?Full
Consume?1604006010
Consume?1312202442
Consume?-1478853208
Consume?1460408111
Consume?1802825495
Queue?is?Empty