Evan's Blog

          Java, software development and others.

            BlogJava :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
            28 隨筆 :: 0 文章 :: 73 評(píng)論 :: 0 Trackbacks
          第3章主要介紹了數(shù)據(jù)的同步(Data Synchronization),這一章則主要介紹線程之間的同步方法(Thread Notification),同樣包括傳統(tǒng)的wait-and-notify方法和JDK 1.5新推出的Condition Variable。在多線程編程中,數(shù)據(jù)同步和線程同步是兩個(gè)最基本也是最關(guān)鍵的部分。
          《Java Threads》一書中通過考察打字程序中當(dāng)按下start和stop按鈕后,每次都創(chuàng)建兩個(gè)新的線程的效率問題來引入線程同步的概念,當(dāng)然不是線程同步的主要用處。不過,教科書歸教科書,實(shí)際運(yùn)用則又是另一回事。所以,通過書本學(xué)習(xí)語法,通過實(shí)踐來獲得運(yùn)用經(jīng)驗(yàn)。

          4.1 Wait and Notify

          1. 等待/喚醒類似于Solaris或POSIX中的條件變量(conditon variables),或者Windows中的事件變量(evant variable)或者信號(hào)量(signal),用于某個(gè)/多個(gè)線程暫停等待某個(gè)條件的滿足,而該條件將由其它線程來設(shè)置的情況。
          2. 在Java中,就像每個(gè)對(duì)象有一個(gè)鎖之外,任何對(duì)象都可以提供等待/喚醒的機(jī)制。就像Java中的synchronized總是表示獲得某個(gè)具體對(duì)象的鎖一樣,wait和notify也總是等待某個(gè)具體的對(duì)象,并由該對(duì)象喚醒;同樣,獲得某個(gè)對(duì)象上的鎖不一定是該對(duì)象需要同步一樣,等待和喚醒的條件也不一定是與之綁定的對(duì)象。
          3. Java中wait-and-notify的幾個(gè)方法:
          void wait(): 使當(dāng)前線程處于等待狀態(tài),直到其它線程調(diào)用了nofity()或者notifyAll()方法為止。
          void wait(long timeout): 使當(dāng)前線程處于等待狀態(tài),直到其它線程調(diào)用了nofity()或者notifyAll()方法,或者超過了指定的時(shí)間(單位為ms)為止
          void wait(long timeout, int nanos):與wait(long)一樣,只是在某些JVM中可以精確到奈秒。
          void notify(): 喚醒一個(gè)正在等待該對(duì)象的線程。
          void notifyAll(): 喚醒所有正在等待該對(duì)象的線程。
          注意:任何等待和喚醒方法都必須在與之對(duì)應(yīng)的對(duì)象的同步方法或同步塊里調(diào)用。即:wait-and-notify必須和與之對(duì)應(yīng)的synchronized關(guān)鍵詞一起使用的。
          4. wait()和sleep()的主要區(qū)別:
            1) sleep()可以在任何地方調(diào)用,而wait()需要在同步方法或同步塊中調(diào)用;
            2) 進(jìn)入wait()函數(shù)時(shí),JVM會(huì)自動(dòng)釋放鎖,而當(dāng)從wait()返回即被喚醒時(shí),又會(huì)自動(dòng)獲得鎖;而sleep()沒有這個(gè)功能,因此如果在wait()的地方用sleep()代替,則會(huì)導(dǎo)致相應(yīng)的nofity()方法在等待時(shí)不可能被觸發(fā),因?yàn)閚otify()必須在相應(yīng)的同步方法或同步塊中,而此時(shí)這個(gè)鎖卻被sleep()所在的方法占用。也就是說,wait-and-notify不可能與sleep()同時(shí)使用。

          4.1.1 The Wait-and-Notify Mechanism and Synchronization
          1. 這一節(jié)詳細(xì)的講解了wait-and-notify機(jī)制和synchronized的關(guān)系,主要是兩點(diǎn):1)wait-and-notify必須和synchronized同時(shí)使用;2)wait()會(huì)自動(dòng)釋放和獲取鎖;
          2. 這一節(jié)中舉了一個(gè)例子用來解釋可能存在當(dāng)條件被不滿足時(shí)也有可能被喚醒的情況:
            1) 線程T1調(diào)用一個(gè)同步方法;
            2) T1檢測(cè)狀態(tài)變量,發(fā)現(xiàn)其不滿足條件;
            3) T1調(diào)用wait(),并釋放鎖;
            4) 線程T2調(diào)用另外一個(gè)同步方法,獲得鎖;
            5) 線程T3調(diào)用另外一個(gè)同步方法,由于T2獲得了鎖,所以處于等待狀態(tài);
            6) T2修改狀態(tài)變量,使其滿足條件,并調(diào)用notify()方法;
            7) T3獲得鎖,然后處理數(shù)據(jù),并將狀態(tài)變量又設(shè)置為不滿足條件的狀態(tài);
            8) T3處理完畢返回;
            9) T1被喚醒,但實(shí)際上此時(shí)條件并不滿足。
          這個(gè)例子剛好印證了《Effective Java》中"Item 50: Never invoke wait outside a loop"和《Practical Java》中"實(shí)踐54:針對(duì)wait()和notifyAll()使用旋鎖(spin locks)"。即總是用下面這種方式來調(diào)用wait():
              synchronized(obj) {
          while(<condition does not hold>)
          wait();
          
          ... // Perform action appropriate to condition }
          或者象《Practical Java》中一樣:
              synchronized(obj) {
          while(<condition does not hold>) {
          try {
          wait();
          } catch (InterruptedException e) {}
          }

          ... // Perform action appropriate to condition }
          3. 調(diào)用wait()的線程T可能在以下幾種情況被喚醒:
            1) 其它線程調(diào)用了notify(),而剛好線程T得到了通知;
            2) 其它線程調(diào)用了notifyAll();
            3) 其它線程中斷了線程T;
            4) 由于JVM的原因,導(dǎo)致了spurious wakeup。

          4.1.2 wait(), notify(), and notifyAll()
          1. 正像多個(gè)線程等待同一對(duì)象上的鎖,當(dāng)鎖釋放時(shí),無法確定哪個(gè)線程會(huì)得到那個(gè)鎖一樣;當(dāng)有多個(gè)線程在wait()時(shí),當(dāng)另外一個(gè)線程調(diào)用nofity()的時(shí)候,也不能確定哪個(gè)線程會(huì)被喚醒; 2. 因此在《Practical Java》的"實(shí)踐53:優(yōu)先使用notifyAll()而非notify()"建議的一樣,結(jié)合實(shí)踐54,可以比較好的解決線程喚醒的問題。

          4.1.3 Wait-and-Notify Mechanism with Synchronized blocks
          再次強(qiáng)調(diào)必須在同一個(gè)對(duì)象的synchronized方法或塊內(nèi)調(diào)用該對(duì)象上的wait和notify方法。

          4.2 Condition Variables

          1. 就像上面反復(fù)強(qiáng)調(diào)的一樣,wait-and-notify機(jī)制是與特定對(duì)象及其上的鎖是綁定在一起的,鎖和喚醒對(duì)象不能分開,這在某些情況下不是很方便;
          2. JDK 1.5提供Condition接口來提供與其它系統(tǒng)幾乎一致的condition variables機(jī)制;
          3. Condition對(duì)象由Lock對(duì)象的newCondition()方法生成,從而允許一個(gè)鎖產(chǎn)生多個(gè)條件變量,可以根據(jù)實(shí)際情況來等待不同條件;
          4. 該書的例子沒有什么特別的實(shí)際意義,但JDK 1.5文檔中提供了一個(gè)例子,能充分說明使用Condition Variables使得程序更加清晰易讀,也更有效率:
          class BoundedBuffer {
          final Lock lock = new ReentrantLock();
          final Condition notFull = lock.newCondition();
          final Condition notEmpty = lock.newCondition(); final Object[] items = new Object[100]; int putptr, takeptr, count;

          public void put(Object x) throws InterruptedException {
          lock.lock();
          try {
          while (count == items.length)
          notFull.await();
          items[putptr] = x;
          if (++putptr == items.length) putptr = 0;
          ++count;
          notEmpty.signal();
          } finally {
          lock.unlock();
          }
          }
          public Object take() throws InterruptedException {
          lock.lock();
          try {
          while (count == 0)
          notEmpty.await();
          Object x = items[takeptr];
          if (++takeptr == items.length) takeptr = 0;
          --count;
          notFull.signal();
          return x;
          } finally {
          lock.unlock();
          }
          }
          }
          具體的說明請(qǐng)參考JDK 1.5的文檔。
          5. 除了用lock和await-and-signal來代替synchronized和wait-and-notify外,其語義和機(jī)制基本一樣。await()在進(jìn)入前也會(huì)自動(dòng)釋放鎖,然后再返回前重新獲得鎖;
          6. 使用Condition Variables的原因:
            1) 如果使用Lock對(duì)象,則必須使用condition variables;
            2) 每個(gè)Lock對(duì)象可以創(chuàng)建多個(gè)condition variable.
          posted on 2006-03-06 22:21 Evan 閱讀(832) 評(píng)論(0)  編輯  收藏 所屬分類: Java筆記
          主站蜘蛛池模板: 喀喇| 房产| 怀安县| 莱西市| 防城港市| 霍邱县| 碌曲县| 辽宁省| 台北市| 双桥区| 西吉县| 逊克县| 金湖县| 紫金县| 白玉县| 万山特区| 板桥市| 安多县| 犍为县| 临湘市| 和平县| 景德镇市| 遂昌县| 大庆市| 清苑县| 岳普湖县| 财经| 灌阳县| 苍山县| 通州区| 沈丘县| 尚义县| 昭觉县| 乌拉特前旗| SHOW| 洪湖市| 封开县| 武威市| 绍兴县| 江西省| 汉源县|