我的家園

          我的家園

          JAVA.util.concurrent 同步框架(翻譯三)

          Posted on 2012-04-15 16:27 zljpp 閱讀(116) 評論(0)  編輯  收藏

          ?

          接上一篇:?

          http://caoyaojun1988-163-com.iteye.com/blog/1290759

          ?

          4、運用:

          AbstractQueuedSynchronizer類將上述功能聯(lián)系在一起,作為一個“模板方法模式[6]”中的模板類,作為其他同步器的基類。子類只是實現(xiàn)預(yù)定義方法,實現(xiàn)通過獲取鎖和釋放鎖的操作來檢查和更新狀態(tài)。然而,AbstractQueuedSynchronizer的子類本身不可用于ADTS,因為這些類暴露的用于內(nèi)部控制獲取和釋放的策略的方法,應(yīng)該對用戶不可見。所有java.util.concurrent包中的同步類聲明了一個私有的內(nèi)部AbstractQueuedSynchronizer子類用于委托它的所有同步方法。這也使得public方法可以根據(jù)不太的同步器給予適當(dāng)?shù)拿Q。

          ?

          例如,假設(shè)最小的Mutex類,當(dāng)同步狀態(tài)為0意味著解鎖,為1意味著鎖定。這個類的同步方法不需要參數(shù)值,因此直接默認使用零。

          ?

          ?

          class Mutex {
          	class Sync extends AbstractQueuedSynchronizer {
          		public boolean tryAcquire(int ignore) {
          			return compareAndSetState(0, 1);
          		}
          		public boolean tryRelease(int ignore) {
          			setState(0); 
          			return true;
          		}
          	}
          	private final Sync sync = new Sync();
          	public void lock() { sync.acquire(0); }
          	public void unlock() { sync.release(0); }
          }
          ?

          ?

          此示例的更全面的版本,以及與其他使用文檔,可以在J2SE文檔中找到,當(dāng)然還有許多變種。例如,tryAcquire可以使用“test-and-test-and-set“改變值之前檢查狀態(tài)值。

          ?

          這可能是令人驚訝的,使用委托(delegation )和虛方法(virtual methods),來構(gòu)造性能敏感的互斥鎖, 然而,各種面向?qū)ο蟮脑O(shè)計,動態(tài)編譯器早已非常成熟;當(dāng)同步器頻繁調(diào)用的是。他們都可以很好的優(yōu)化掉這個開銷;AbstractQueuedSynchronizer類還提供了一些方法,可以設(shè)置同步類的控制策略。例如,基于acquire方法的超時和中斷的版本、獨占模式的同步器、鎖。AbstractQueuedSynchronizer類也提供了一系列的方法(如acquireShared),他們與tryAcquireShared和tryReleaseShared方法不同,可以通知框架(通過其返回值)將來的acquires方法是否可以成功;最終可以實現(xiàn)級聯(lián)信號喚醒多個線程。

          ?

          盡管序列化(永久存儲或傳輸)同步器通常是不明智的,但是由于這些類通常用于構(gòu)建其他類,如線程安全的集合。所以通常實現(xiàn)序列化。AbstractQueuedSynchronizer和ConditionObject類提供的方法來序列化同步狀態(tài),而不是被阻塞線程的底層或其他本質(zhì)上臨時狀態(tài)。即便如此,大多數(shù)的同步類反序列化時只是復(fù)位同步狀態(tài)作為初始值,與內(nèi)置鎖反序列化時總是設(shè)置為解鎖狀態(tài)的隱含的政策相同。雖然不是必須的,但仍明確支持final域的反序列化。

          ?

          4.1 公平性

          ?

          雖然他們是基于FIFO隊列,同步器不一定是公平的。可以注意到,在基本的acquire算法中(3.3節(jié)),tryAcquire是再排隊前進行檢查。因此,新請求獲取鎖(acquiring)的線程可以優(yōu)先于隊列的頭部第一個節(jié)點對應(yīng)的線程。

          ?

          這雖然破壞了FIFO的策略,但是也有普遍高于其他技術(shù)的總吞吐量。這減少了有鎖可以用,但是因為預(yù)定的下一個線程還在喚醒(unblocking)的過程中,所以還沒有線程獲取到鎖的時間,同時,它通過只允許一個(隊列的第一個)線程喚醒,避免了過度的、無用的、競爭;可以實現(xiàn)自己的tryAcquire方法,在交回控制權(quán)前簡單的多試幾次,這樣可以加劇不公平性, 如果有需要,開發(fā)者可以自己創(chuàng)建一個自己的簡單持有的同步器;

          ?

          FIFO同步器是相對最公平的鎖;即便它會被打破,一個等待喚醒的(unpark)的線程與所有打破規(guī)則進入的線程都有一個公平的競爭機會,如果失敗它會重新阻塞;當(dāng)然,如果闖入的線程比獲取一個等待喚醒的線程喚醒到達的快,隊列中第一個線程幾乎沒有贏的概率,所以幾乎總是reblock, 簡單持有的同步器,通常用于多個闖入線程和多個等待喚醒的線程在多處理器的場景,此時隊列中的第一個線程被喚醒;如下圖所示,既要維持一個或多個線程時處理器的利用率,同時也要避免饑餓。


          ?

          如果需要更加公平的策略,也相對比較簡單。程序員可以自己定義 tryAcquire,如果不是隊列的head節(jié)點就失敗(返回false),達到嚴格的公平;可以通過少數(shù)提供的檢查方法之一getFirstQueuedThread,來檢查是否是第一個節(jié)點。

          ?

          一個更快,更嚴格的變種是如果隊列(暫時)是空,也讓tryAcquire成功,在這種情況下,如果多個線程遇到一個空隊列的情況下競爭,其中至少有一個及第一個得到執(zhí)行權(quán)的線程不需要入隊。這一策略使得在所有java.util.concurrent中的同步器,支持“公平”的模式。

          ?

          雖然在實踐中往往希望公平,但是即便設(shè)置公平也不會得到保證,因為Java語言規(guī)范并不提供調(diào)度保證。例如,即使有一個嚴格意義上的公平同步,如果他們從來不需要阻止等待對方,JVM可以純粹按照順序選擇運行線程,而實際上,在單處理器的環(huán)境中,這些線程在被上下文切換之前可以每次運行一個時間片。如果有一個線程持有一個互斥鎖,它必須暫時先得到時間片才能釋放鎖,不然就阻塞那些需要鎖的線程,這導(dǎo)致延長了同步器是可用的,但是不能被線程獲取到的時間;使用公平的同步器往往在多處理器有很好的表現(xiàn),因為這樣有更好的并發(fā),因此出現(xiàn)競爭的幾率更多。

          ?

          即使他們在高競爭的場景下,性能不是很理想,但是公平鎖任然可以工作的很好,同時也保持編碼的簡潔。例如:當(dāng)維護相對較長的代碼或者延長鎖間的時間間隔,在這種情況下,可以提示一定的性能,但是饑餓的風(fēng)險更大。同步框架將最終的決定權(quán)交由用戶。

          ?

          4.2 同步器

          ?

          這里描述一個草圖關(guān)于怎樣使用這個框架定義java.util.concurrent中的同步器;ReentrantLock類使用同步狀態(tài)(遞歸)維護鎖的數(shù)量。在獲取鎖的時候,它會記錄當(dāng)前線程的ID,并且遞歸檢查是否有異常線程獲取鎖導(dǎo)致的非法的狀態(tài)異常。該類也提供ConditionObject,暴露它的監(jiān)測和檢查方法。同時支持“公平”的模式的選項,在內(nèi)部通過實現(xiàn)兩個不同AbstractQueuedSynchronizer的子類(公平的實現(xiàn)不允許打破規(guī)則),并為每個ReentrantLock的實例選擇以個合適的實現(xiàn)。

          ?

          ReentrantReadWriteLock類使用16位的同步狀態(tài)持有寫鎖定計數(shù),其余16位持有讀鎖計數(shù)。WriteLock使用與ReentrantLock同樣的結(jié)構(gòu)。ReadLock使用acquireShared的方法,以允許多個讀者。

          ?

          Semaphore類(一個計數(shù)信號)使用同步狀態(tài)保持當(dāng)前計數(shù)。它定義acquireShared方法遞減計數(shù),如果為負數(shù)了就阻塞當(dāng)前線程;同樣使用tryRelease遞增計數(shù),如果是正數(shù)的時候,就喚醒線程。

          ?

          CountDownLatch類使用同步狀態(tài)代表計數(shù)。當(dāng)減到零的時候所有所有獲取鎖的操作全部成功返回。

          ?

          FutureTask類使用同步狀態(tài)來代表future 的運行狀態(tài)(初始,運行,注銷,完成)。調(diào)用釋放(release)來設(shè)置或取消一個future ,等待線程處理完成,acquire返回喚醒線程。

          ?

          SynchronousQueue類(一個CSP風(fēng)格的切換)使用內(nèi)部等待節(jié)點調(diào)整生產(chǎn)者和消費者;它采用了同步狀態(tài)來作為標識,當(dāng)消費者改變狀態(tài)的時候允許生產(chǎn)者繼續(xù)處理,反之亦然。

          ?

          java.util.concurrent包的用戶當(dāng)然可以為自己的應(yīng)用程序定義自己的同步器;例如,在那些經(jīng)常遇到,但是包里沒有提供的各種Win32事件的語義類,二進制閂鎖,鎖集中管理,并基于樹的壁壘。

          ?

          原文見第一篇的附件

          下一篇:http://caoyaojun1988-163-com.iteye.com/blog/1315089






          只有注冊用戶登錄后才能發(fā)表評論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 衡阳县| 华蓥市| 吕梁市| 榕江县| 洛浦县| 建宁县| 观塘区| 克东县| 拉萨市| 鄂托克前旗| 察哈| 文成县| 衡阳市| 芜湖县| 富阳市| 建昌县| 河北区| 贡觉县| 大田县| 双江| 芷江| 内乡县| 河北区| 福泉市| 桐城市| 绵阳市| 集安市| 西华县| 湟中县| 庄河市| 东平县| 观塘区| 新巴尔虎左旗| 三穗县| 唐海县| 漠河县| 罗源县| 大田县| 巴彦县| 集安市| 赞皇县|