jimingminlovefly

          統計

          最新評論

          wait/notify/synchronized

          為了說清道理,我們用“圖書館借書”這個經典例子來作解釋。 

              一本書同時只能借給一個人。現在有一本書,圖書館已經把這本書借了張三。 

              在簡單的synchrnozed 同步機制下,李四如果想借,先要去圖書館查看書有沒有還回來。李四是個心急的人,他每天都去圖書館查;而張三看書看得慢,過了半個月才把書還回來,結果李四在這半個月里全都白跑了,浪費了不少交通車費 

              而如果使用wait/notify機制,李四就不用白忙了。他第一次去圖書館時發現書已借走,就回家靜靜等待(wait);張三把書還掉后,通知(notify)李四,李四去圖書館拿書即可。整個過程中,李四沒有白跑,沒浪費錢。 

              回到計算機世界: 
              
                 書           --  臨界資源,需互斥地訪問 
              張三,李四      --  兩個競爭的線程 
          坐車去圖書館查書   --  輪詢 
                車費          --  CPU空間 
                等待          --  wait 
            通知下一個借書者   --  notify 

              也就是說,若使用簡單的synchonized機制實現互斥,會導致線程主動發起輪詢,若N次輪詢沒有成功,就產生了N次的CPU空間浪費;如果加上了 wait/notify機制,就可以避免這些無謂的輪詢,節省CPU的消耗。

           

          附:

          java,wait與notify的區別
          wait與notify是java同步機制中重要的組成部分。結合與synchronized關鍵字使用,可以建立很多優秀的同步模型。
               synchronized(this){}等價與public synchronized void method(){.....}
               同步分為類級別和對象級別,分別對應著類鎖和對象鎖。類鎖是每個類只有一個,如果static的方法被synchronized關鍵字修飾,則在這個方法被執行前必須獲得類鎖;對象鎖類同。(static synchronized是類級別的,非static的synchronized和synchronized塊都是對象級別的,即作用在同一new出來的對象上)
               首先,調用一個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,但是這個還是有順序的,每個對象都保存這一個等待對象鏈,調用的順序就是這個鏈的順序。其實啟動等待對象鏈中各個線程的也是一個線程,在具體應用的時候,需要注意一下。


          當線程A獲得了obj鎖后,發現條件condition不滿足,無法繼續下一處理,于是線程A就wait()。
          在另一線程B中,如果B更改了某些條件,使得線程A的condition條件滿足了,就可以喚醒線程A:
          synchronized(obj) {condition = true;obj.notify();} 

          需要注意的概念是:
          ◆調用obj的wait(), notify()方法前,必須獲得obj鎖,也就是必須寫在synchronized(obj) {...} 代碼段內。
          ◆調用obj.wait()后,線程A就釋放了obj的鎖,否則線程B無法獲得obj鎖,也就無法在synchronized(obj) {...} 代碼段內喚醒A。
          ◆當obj.wait()方法返回后,線程A需要再次獲得obj鎖,才能繼續執行。
          ◆如果A1,A2,A3都在obj.wait(),則B調用obj.notify()只能喚醒A1,A2,A3中的一個(具體哪一個由JVM決定)。
          ◆obj.notifyAll()則能全部喚醒A1,A2,A3,但是要繼續執行obj.wait()的下一條語句,必須獲得obj鎖,因此,A1,A2,A3只有一個有機會獲得鎖繼續執行,例如A1,其余的需要等待A1釋放obj鎖之后才能繼續執行。
          ◆當B調用obj.notify/notifyAll的時候,B正持有obj鎖,因此,A1,A2,A3雖被喚醒,但是仍無法獲得obj鎖。直到B退出synchronized塊,釋放obj鎖后,A1,A2,A3中的一個才有機會獲得鎖繼續執行。

          posted on 2013-01-15 17:24 計明敏 閱讀(235) 評論(0)  編輯  收藏 所屬分類: j2se

          主站蜘蛛池模板: 正蓝旗| 揭东县| 弥渡县| 唐山市| 宜黄县| 迭部县| 金溪县| 渝北区| 稷山县| 高淳县| 武冈市| 策勒县| 岢岚县| 鄂尔多斯市| 玛多县| 瑞金市| 宜黄县| 婺源县| 莆田市| 桦川县| 阿坝| 九龙县| 班戈县| 乡城县| 东安县| 宜城市| 邢台县| 白朗县| 惠安县| 布拖县| 和林格尔县| 宝清县| 陆河县| 霍林郭勒市| 西盟| 肥乡县| 滨海县| 平舆县| 资中县| 衡水市| 遂川县|