在Java中,全部對象都可以被做爲"監視器monitor"——指一個擁有一個獨佔鎖,一個入口隊列和一個等待隊列的實體entity。全部對象的非同步方法都可以在任意時刻被任意線程調用,此時不須要考慮加鎖的問題。而對於對象的同步方法來講,在任意時刻有且僅有一個擁有該對象獨佔鎖的線程可以調用它們。例如,一個同步方法是獨佔的。若是在線程調用某一對象的同步方法時,對象的獨佔鎖被其餘線程擁有,那麼當前線程將處於阻塞狀態,並添加到對象的入口隊列中。java
只有在調用線程擁有某個對象的獨佔鎖時,纔可以調用該對象的wait(),notify()和notifyAll()方法。這一點一般不會被程序員注意,由於程序驗證一般是在對象的同步方法或同步代碼塊中調用它們的。若是嘗試在未獲取對象鎖時調用這三個方法,那麼你將獲得一個"java.lang.IllegalMonitorStateException:current thread not owner"。程序員
當一個線程正在某一個對象的同步方法中運行時調用了這個對象的wait()方法,那麼這個線程將釋放該對象的獨佔鎖並被放入這個對象的等待隊列。注意,wait()方法強制當前線程釋放對象鎖。這意味着在調用某對象的wait()方法以前,當前線程必須已經得到該對象的鎖。所以,線程必須在某個對象的同步方法或同步代碼塊中才能調用該對象的wait()方法。多線程
當某線程調用某對象的notify()或notifyAll()方法時,任意一個(對於notify())或者全部(對於notifyAll())在該對象的等待隊列中的線程,將被轉移到該對象的入口隊列。接着這些隊列(譯者注:可能只有一個)將競爭該對象的鎖,最終得到鎖的線程繼續執行。若是沒有線程在該對象的等待隊列中等待得到鎖,那麼notify()和notifyAll()將不起任何做用。在調用對象的notify()和notifyAll()方法以前,調用線程必須已經獲得該對象的鎖。所以,必須在某個對象的同步方法或同步代碼塊中才能調用該對象的notify()或notifyAll()方法。this
對於處於某對象的等待隊列中的線程,只有當其餘線程調用此對象的notify()或notifyAll()方法時纔有機會繼續執行。線程
調用wait()方法的緣由一般是,調用線程但願某個特殊的狀態(或變量)被設置以後再繼續執行。調用notify()或notifyAll()方法的緣由一般是,調用線程但願告訴其餘等待中的線程:"特殊狀態已經被設置"。這個狀態做爲線程間通訊的通道,它必須是一個可變的共享狀態(或變量)。code
例如,生產者線程向緩衝區中寫入數據,消費者線程從緩衝區中讀取數據。消費者線程須要等待直到生產者線程完成一次寫入操做。生產者線程須要等待消費者線程完成一次讀取操做。假設wait(),notify(),notifyAll()方法不須要加鎖就可以被調用。此時消費者線程調用wait()正在進入狀態變量的等待隊列(譯者注:可能還未進入)。在同一時刻,生產者線程調用notify()方法打算向消費者線程通知狀態改變。那麼此時消費者線程將錯過這個通知並一直阻塞。所以,對象的wait(),notify(),notifyAll()方法必須在該對象的同步方法或同步代碼塊中被互斥地調用。對象
三。 wait()與sleep()的區別
sleep()方法是Thread類的靜態方法,不涉及到線程間同步概念,僅僅爲了讓一個線程自身得到一段沉睡時間。sleep能夠在任何地方使用。
wait()方法是object類的方法,解決的問題是線程間的同步,該過程包含了同步鎖的獲取和釋放,調用wait方法將會將調用者的線程掛起,直到其餘線程調用同一個對象的notify()方法纔會從新激活調用者。
注意:線程調用notify()以後,只有該線程徹底從 synchronized代碼裏面執行完畢後,monitor纔會被釋放,被喚醒線程才能夠真正獲得執行權。隊列
今天同窗寫個手機遊戲,用藍牙傳輸數據的時候丟包,問我解決方案,我提出的方案是:用多線程發送數據並要求對方回送ack號,若是在必定時間內沒收到就要重發,若是收到了就要自身wait,那麼這要用到多線了,開始寫了幾個總是報錯,就在網上找了找這方面的資料,終於解決了,呵呵!下面我把這篇寫的比較全面的文章轉載過來,作個筆記,但願能幫助更多的用多線程出現問題的朋友們。遊戲
wait與notify是java同步機制中重要的組成部分。結合與synchronized關鍵字使用,能夠創建不少優秀的同步模型。資源
synchronized(this){}等價與public synchronized void method(){.....}
同步分爲類級別和對象級別,分別對應着類鎖和對象鎖。類鎖是每一個類只有一個,若是static的方法被synchronized關鍵字修飾,則在這個方法被執行前必須得到類鎖;對象鎖類同。
首先,調用一個Object的wait與notify/notifyAll的時候,必須保證調用代碼對該Object是同步的,也就是說必須在做用等同於synchronized(obj){......}的內部纔可以去調用obj的wait與notify/notifyAll三個方法,不然就會報錯:
java.lang.IllegalMonitorStateException: current thread not owner
在調用wait的時候,線程自動釋放其佔有的對象鎖,同時不會去申請對象鎖。當線程被喚醒的時候,它纔再次得到了去得到對象鎖的權利。
因此,notify與notifyAll沒有太多的區別,只是notify僅喚醒一個線程並容許它去得到鎖,notifyAll是喚醒全部等待這個對象的線程並容許它們去得到對象鎖,只要是在synchronied塊中的代碼,沒有對象鎖是步履維艱的。其實喚醒一個線程就是從新容許這個線程去得到對象鎖並向下運行。
順便說一下notifyall,雖然是對每一個wait的對象都調用一次notify,可是這個仍是有順序的,每一個對象都保存這一個等待對象鏈,調用的順序就是這個鏈的順序。其實啓動等待對象鏈中各個線程的也是一個線程,在具體應用的時候,須要注意一下。
class ThreadA{ public static void main(String[] args){ ThreadB b = new ThreadB(); b.start(); System.out.println("b is start...."); synchronized (b){// 括號裏的b是什麼意思,起什麼做用? try{ System.out.println("Waiting for b to complete..."); b.wait();// 這一句是什麼意思,究竟讓誰wait? System.out.println("Completed.Now back to main thread"); } catch (InterruptedException e) { } } System.out.println("Total is :" + b.total); } } class ThreadB extends Thread{ int total; public void run(){ synchronized (this){ System.out.println("ThreadB is running.."); for (int i = 0; i < 100; i++){ total += i; System.out.println("total is " + total); } notify(); } } }
要分析這個程序,首先要理解notify()和wait(),爲何在前幾天紀錄線程的時候沒有紀錄這兩個方法呢,由於這兩個方法原本就不屬於Thread類,而是屬於最底層的object基礎類的,也就是說不光是Thread,每一個對象都有notify和wait的功能,爲何?由於他們是用來操縱鎖的,而每一個對象都有鎖,鎖是每一個對象的基礎,既然鎖是基礎的,那麼操縱鎖的方法固然也是最基礎了.
再往下看以前呢,首先最好複習一下Think in Java的14.3.1中第3部份內容:等待和通知,也就是wait()和notify了.
按照Think in Java中的解釋:"wait()容許咱們將線程置入「睡眠」狀態,同時又「積極」地等待條件發生改變.並且只有在一個notify()或notifyAll()發生變化的時候,線程纔會被喚醒,並檢查條件是否有變."
咱們來解釋一下這句話.
"wait()容許咱們將線程置入「睡眠」狀態",也就是說,wait也是讓當前線程阻塞的,這一點和sleep或者suspend是相同的.那和sleep,suspend有什麼區別呢?
區別在於"(wait)同時又「積極」地等待條件發生改變",這一點很關鍵,sleep和suspend沒法作到.由於咱們有時候須要經過同步(synchronized)的幫助來防止線程之間的衝突,而一旦使用同步,就要鎖定對象,也就是獲取對象鎖,其它要使用該對象鎖的線程都只能排隊等着,等到同步方法或者同步塊裏的程序所有運行完纔有機會.在同步方法和同步塊中,不管sleep()仍是suspend()都不可能本身被調用的時候解除鎖定,他們都霸佔着正在使用的對象鎖不放.
而wait卻能夠,它可讓同步方法或者同步塊暫時放棄對象鎖,而將它暫時讓給其它須要對象鎖的人(這裏應該是程序塊,或線程)用,這意味着可在執行wait()期間調用線程對象中的其餘同步方法!在其它狀況下(sleep啊,suspend啊),這是不可能的.
可是注意我前面說的,只是暫時放棄對象鎖,暫時給其它線程使用,我wait所在的線程仍是要把這個對象鎖收回來的呀.wait什麼?就是wait別人用完了還給我啊!
好,那怎麼把對象鎖收回來呢?
第一種方法,限定借出去的時間.在wait()中設置參數,好比wait(1000),以毫秒爲單位,就代表我只借出去1秒中,一秒鐘以後,我自動收回.
第二種方法,讓借出去的人通知我,他用完了,要還給我了.這時,我立刻就收回來.哎,假如我設了1小時以後收回,別人只用了半小時就完了,那怎麼辦呢?靠!固然用完了就收回了,還管我設的是多長時間啊.
那麼別人怎麼通知我呢?相信你們均可以想到了,notify(),這就是最後一句話"並且只有在一個notify()或notifyAll()發生變化的時候,線程纔會被喚醒"的意思了.
所以,咱們可將一個wait()和notify()置入任何同步方法或同步塊內部,不管在那個類裏是否準備進行涉及線程的處理。並且實際上,咱們也只能在同步方法或者同步塊裏面調用wait()和notify().
這個時候咱們來解釋上面的程序,簡直是易如反掌了.
synchronized(b){...};的意思是定義一個同步塊,使用b做爲資源鎖。b.wait();的意思是臨時釋放鎖,並阻塞當前線程,好讓其餘使用同一把鎖的線程有機會執行,在這裏要用同一把鎖的就是b線程自己.這個線程在執行到必定地方後用notify()通知wait的線程,鎖已經用完,待notify()所在的同步塊運行完以後,wait所在的線程就能夠繼續執行