Inside AbstractQueuedSynchronizer (1)

1 Overview java

    若是查看ReentrantLock,CountDownLatch,Semaphore,FutureTask,ThreadPoolExecutor的源碼,都會發現有個名叫Sync的靜態內部類,繼承自AbstractQueuedSynchronizer。實際上AbstractQueuedSynchronizer是java.util.concurrent的核心組件之一,它爲併發包中的其餘synchronizers提供了一組公共的基礎設施。 併發

 

2 LockSupport 
    在介紹AbstractQueuedSynchronizer以前,首先要介紹一下java.util.concurrent.locks.LockSupport。在LockSupport出現以前,若是要block/unblock某個Thread,除了使用Java語言內置的monitor機制以外,只能經過Thread.suspend()和Thread.resume()。然而Thread.suspend()和Thread.resume()基本上不可用,除了可能致使死鎖以外,它們還存在一個沒法解決的競爭條件:若是在調用Thread.suspend()以前調用了Thread.resume(),那麼該Thread.resume()調用沒有任何效果。LockSupport最主要的做用,即是經過一個許可(permit)狀態,解決了這個問題。 工具

 

      那麼LockSupport和Java語言內置的monitor機制有什麼區別呢?它們的語義是不一樣的。LockSupport是針對特定Thread來進行block/unblock操做的;wait()/notify()/notifyAll()是用來操做特定對象的等待集合的。爲了防止知識生鏽,在這裏簡單介紹一下Java語言內置的monitor機制(詳見:http://whitesock.iteye.com/blog/162344 )。正如每一個Object都有一個鎖, 每一個Object也有一個等待集合(wait set),它有wait、notify、notifyAll和Thread.interrupt方法來操做。同時擁有鎖和等待集合的實體,一般被成爲監視器(monitor)。每一個Object的等待集合是由JVM維護的。等待集合一直存放着那些由於調用對象的wait方法而被阻塞的線程。因爲等待集合和鎖之間的交互機制,只有得到目標對象的同步鎖時,才能夠調用它的wait、notify和notifyAll方法。這種要求一般沒法靠編譯來檢查,若是條件不能知足,那麼在運行的時候調用以上方法就會致使其拋出IllegalMonitorStateException。 spa

 

    wait() 方法被調用後,會執行以下操做: .net

  • 若是當前線程已經被中斷,那麼該方法馬上退出,而後拋出一個InterruptedException異常。不然線程會被阻塞。
  • JVM把該線程放入目標對象內部且沒法訪問的等待集合中。
  • 目標對象的同步鎖被釋放,可是這個線程鎖擁有的其餘鎖依然會被這個線程保留着。當線程從新恢復質執行時,它會從新得到目標對象的同步鎖。

    notify()方法被調用後,會執行以下操做: 線程

  • 若是存在的話,JVM會從目標對象內部的等待集合中任意移除一個線程T。若是等待集合中的線程數大於1,那麼哪一個線程被選中徹底是隨機的。
  • T必須從新得到目標對象的同步鎖,這必然致使它將會被阻塞到調用Thead.notify()的線程釋放該同步鎖。若是其餘線程在T得到此鎖以前就得到它,那麼T就要一直被阻塞下去。
  • T從執行wait()的那點恢復執行。

    notifyAll()方法被調用後的操做和notify()相似,不一樣的只是等待集合中全部的線程(同時)都要執行那些操做。然而等待集合中的線程必需要在競爭到目標對象的同步鎖以後,才能繼續執行。 對象

 

    LockSupport類中比較重要的方法有以下幾個: blog

Java代碼   收藏代碼
  1. public static void park() {  
  2.     unsafe.park(false, 0L);  
  3. }  
  4.   
  5. public static void park(Object blocker) {  
  6.     Thread t = Thread.currentThread();  
  7.     setBlocker(t, blocker);  
  8.     unsafe.park(false, 0L);  
  9.     setBlocker(t, null);  
  10. }  
  11.   
  12. public static void unpark(Thread thread) {  
  13.     if (thread != null)  
  14.         unsafe.unpark(thread);  
  15. }  

    其中park()和park(Object blocker)方法用於block當前線程,unpark(Thread thread)方法用於unblock制定的線程。 跟Thread.suspend()和Thread.resume()不一樣的是,LockSupport經過許可(permit)機制保證:若是當前線程擁有許可,那麼park系列方法會消費掉該許可,而且當即返回(不會被阻塞)。也就是說以下代碼在執行的時候,不會被阻塞: 繼承

Java代碼   收藏代碼
  1. LockSupport.unpark(Thread.currentThread());  
  2. LockSupport.park();  

    須要注意的是:許可不會被累計。也就是說在park調用以前的屢次unpark調用,只會unblock一次park調用。即如下代碼會被阻塞: 文檔

Java代碼   收藏代碼
  1. LockSupport.unpark(Thread.currentThread());  
  2. LockSupport.unpark(Thread.currentThread());  
  3. LockSupport.park();  
  4. LockSupport.park();  

    關於park()和park(Object blocker)的區別,Object blocker參數的做用在於容許記錄當前線程被阻塞的緣由,以便監控分析工具進行分析。官方的文檔中也更建議使用park(Object blocker)。此外,跟Object.wait()方法同樣,park系列方法也會由於僞喚醒的緣由返回。

相關文章
相關標籤/搜索