Inside AbstractQueuedSynchronizer (1)
Inside AbstractQueuedSynchronizer (2)
Inside AbstractQueuedSynchronizer (3)
Inside AbstractQueuedSynchronizer (4)
1 Overview
如果查看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。
wait() 方法被調用后,會執行如下操作:
- 如果當前線程已經被中斷,那么該方法立刻退出,然后拋出一個InterruptedException異常。否則線程會被阻塞。
- JVM把該線程放入目標對象內部且無法訪問的等待集合中。
- 目標對象的同步鎖被釋放,但是這個線程鎖擁有的其他鎖依然會被這個線程保留著。當線程重新恢復質執行時,它會重新獲得目標對象的同步鎖。
notify()方法被調用后,會執行如下操作:
- 如果存在的話,JVM會從目標對象內部的等待集合中任意移除一個線程T。如果等待集合中的線程數大于1,那么哪個線程被選中完全是隨機的。
- T必須重新獲得目標對象的同步鎖,這必然導致它將會被阻塞到調用Thead.notify()的線程釋放該同步鎖。如果其他線程在T獲得此鎖之前就獲得它,那么T就要一直被阻塞下去。
- T從執行wait()的那點恢復執行。
notifyAll()方法被調用后的操作和notify()類似,不同的只是等待集合中所有的線程(同時)都要執行那些操作。然而等待集合中的線程必須要在競爭到目標對象的同步鎖之后,才能繼續執行。
LockSupport類中比較重要的方法有如下幾個:
public static void park() { unsafe.park(false, 0L); } public static void park(Object blocker) { Thread t = Thread.currentThread(); setBlocker(t, blocker); unsafe.park(false, 0L); setBlocker(t, null); } public static void unpark(Thread thread) { if (thread != null) unsafe.unpark(thread); }
其中park()和park(Object blocker)方法用于block當前線程,unpark(Thread thread)方法用于unblock制定的線程。 跟Thread.suspend()和Thread.resume()不同的是,LockSupport通過許可(permit)機制保證:如果當前線程擁有許可,那么park系列方法會消費掉該許可,并且立即返回(不會被阻塞)。也就是說如下代碼在執行的時候,不會被阻塞:
LockSupport.unpark(Thread.currentThread()); LockSupport.park();
需要注意的是:許可不會被累計。也就是說在park調用之前的多次unpark調用,只會unblock一次park調用。即以下代碼會被阻塞:
LockSupport.unpark(Thread.currentThread()); LockSupport.unpark(Thread.currentThread()); LockSupport.park(); LockSupport.park();
關于park()和park(Object blocker)的區別,Object blocker參數的作用在于允許記錄當前線程被阻塞的原因,以便監控分析工具進行分析。官方的文檔中也更建議使用park(Object blocker)。此外,跟Object.wait()方法一樣,park系列方法也會因為偽喚醒的原因返回。