1. 程式人生 > >由AbstractQueuedSynchronizer和ReentrantLock來看模版方法模式

由AbstractQueuedSynchronizer和ReentrantLock來看模版方法模式

however 結構圖 debug exception alt strac red con overhead

  在學完volatile和CAS之後,近幾天在擼AbstractQueuedSynchronizer(AQS)的源代碼,很多並發工具都是基於AQS來實現的,這也是並發專家Doug Lea的初衷,通過寫一個這樣的基礎工具來提高j.u.c的靈活性。具體可以看這篇論文的一段原文,我摘錄一下:

  As is well-known (see e.g., [2]) nearly any synchronizer can be used to implement nearly any other. For example, it is possible to build semaphores from reentrant locks, and vice versa. However,doing so often entails enough complexity, overhead, and inflexibility to be at best a second-rate engineering option.Further, it is conceptually unattractive. If none of these constructs are intrinsically more primitive than the others, developers should not be compelled to arbitrarily choose one of them as a basis for building others. Instead, JSR166 establishes a small framework centered on class AbstractQueuedSynchronizer,that provides common mechanics that are used by most of the provided synchronizers in the package, as well as other classes that users may define themselves.

  翻譯下來大致的意思就是,如果使用信號量、鎖等互為彼此實現,這樣一種設計只會讓問題變得復雜而且缺乏靈活性,既然這樣,JSR166提出了AQS來作為基礎工具來對外提供一種通用的機制。

AQS的設計和實現非常復雜,所以我打算通過DEBUG的方式看下內部是如何實現的,在看源碼的過程中順便學習了一個設計模式-模板方法。

1、模板方法結構

模板方法的本質其實就是類的繼承,模板方法模式需要開發抽象類和具體子類的設計之間的協作,我們實際工作中應該有很多這樣的場景,比如我們通常會在業務邏輯層定義好Service類的抽象方法,而實際的業務邏輯實現會交給ServiceImpl類。模板方法模式的類結構圖大致如下:

技術分享圖片

      圖一

  抽象模板(AbstractTemplate)角色有如下責任:

  定義了一個或多個抽象操作,以便讓子類實現。這些抽象操作叫做基本操作,它們是一個頂級邏輯的組成步驟。定義並實現了一個模板方法,這個模板方法一般是一個具體方法,它給出了一個頂級邏輯的骨架,而邏輯的組成步驟在相應的抽象操作中,推遲到子類實現。頂級邏輯也有可能調用一些具體方法。AbstractQueuedSynchronizer就是這樣一個抽象模板。

  具體模板(ConcreteTemplate)角色又有如下責任:

  實現父類所定義的一個或多個抽象方法,它們是一個頂級邏輯的組成步驟。每一個抽象模板角色都可以有任意多個具體模板角色與之對應,而每一個具體模板角色都可以給出這些抽象方法(也就是頂級邏輯的組成步驟)的不同實現,從而使得頂級邏輯的實現各不相同。ReentrantLock算是這樣一個具體模板,不過ReentrantLock把自己的實現又委托給了內部實現類Sync。

  模板模式的關鍵是:子類可以置換掉父類的可變部分,但是子類卻不可以改變模板方法所代表的頂級邏輯。每當定義一個新的子類時,不要按照控制流程的思路去想,而應當按照“責任”的思路去想。換言之,應當考慮哪些操作是必須置換掉的,哪些操作是可以置換掉的,以及哪些操作是不可以置換掉的。使用模板模式可以使這些責任變得清晰。

2、模板方法模式中的方法

  模板方法中的方法可以分為兩大類:模板方法和基本方法

  模板方法:一個模板方法是定義在抽象類中的,把基本操作方法組合在一起形成一個總算法或一個總行為的方法。一個抽象類可以有任意多個模板方法,而不限於一個。每一個模板方法都可以調用任意多個具體方法。

  基本方法:基本方法又可以分為三種:抽象方法(Abstract Method)、具體方法(Concrete Method)和鉤子方法(Hook Method)

  抽象方法:一個抽象方法由抽象類聲明,由具體子類實現。在Java語言裏抽象方法以abstract關鍵字標示。

  具體方法:一個具體方法由抽象類聲明並實現,而子類並不實現或置換。

  鉤子方法:一個鉤子方法由抽象類聲明並實現,而子類會加以擴展。通常抽象類給出的實現是一個空實現,作為方法的默認實現。

3、由AbstractQueuedSynchronizer和ReentrantLock來看模版方法模式

  以ReentrantLock的lock方法為例來看下模板方法的體現。具體代碼如下:

public class MutexDemo {
//  private static Mutex mutex = new Mutex();
    private static ReentrantLock mutex = new ReentrantLock();
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            Thread thread = new Thread(() -> {
                mutex.lock();
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    mutex.unlock();
                }
            });
            thread.start();
        }
    }
}

  默認是以非公平鎖來初始化ReentrantLock,所以方法中ReentrantLock將自己的行為委托給NonfairSync,NonfairSync繼承Sync,Sync又繼承了AbstractQueuedSynchronizer。在整個過程中AbstractQueuedSynchronizer的方法acquire其實就是模板模式裏面的模板方法,方法release也是一樣的,這兩個方法是整套框架裏面的頂級邏輯。在這個頂級邏輯之外,Sync給出了lock方法,這是一個抽象方法,下設2種實現,分別是公平鎖FairSync和非公平鎖NonfairSync。在頂級邏輯裏面還有方法的具體方法實現,比如addWaiter和acquireQueued。另外還有一個鉤子方法,如tryAcquire,在AQS裏面這個鉤子方法是沒有具體的實現的,具體的實現交給了NonfairSync。感興趣的讀者可以按照這思路跟蹤下代碼,然後理解一下模板方法這個設計模式。

參考資料:

https://github.com/lingjiango/ConcurrentProgramPractice

http://www.cnblogs.com/java-my-life/archive/2012/05/14/2495235.html

由AbstractQueuedSynchronizer和ReentrantLock來看模版方法模式