AbstractQueuedSynchronizer

理解多線程的并發(fā)鎖,可結(jié)合多進(jìn)程的分布式鎖(如Zookeeper的互斥鎖、讀寫(xiě)鎖的實(shí)現(xiàn)原理),本質(zhì)是相通的

介紹

AQS,AbstractQueuedSynchronizer,即隊(duì)列同步器。它是構(gòu)建鎖或者其他同步組件的基礎(chǔ)框架(如ReentrantLock、CountDownLatch、CyclicBarrier、ReentrantReadWriteLock、Semaphore等),JUC并發(fā)包的作者(Doug Lea)期望它能夠成為實(shí)現(xiàn)大部分同步需求的基礎(chǔ)。它是JUC并發(fā)包中的核心基礎(chǔ)組件。

看一眼注釋

/**
* Provides a framework for implementing blocking locks and related
 * synchronizers (semaphores, events, etc) that rely on
 * first-in-first-out (FIFO) wait queues.  This class is designed to
 * be a useful basis for most kinds of synchronizers that rely on a
 * single atomic {@code int} value to represent state.
**/
  • AQS使用一個(gè)int類(lèi)型的成員變量state來(lái)表示同步狀態(tài),當(dāng)state>0時(shí)表示已經(jīng)獲取了鎖,當(dāng)state = 0時(shí)表示釋放了鎖。它提供了三個(gè)方法(getState()、setState(int newState)、compareAndSetState(int expect,int update))來(lái)對(duì)同步狀態(tài)state進(jìn)行操作,當(dāng)然AQS可以確保對(duì)state的操作是安全的。

  • AQS通過(guò)內(nèi)置的FIFO同步隊(duì)列來(lái)完成資源獲取線程的排隊(duì)工作,如果當(dāng)前線程獲取同步狀態(tài)失敗(鎖)時(shí),AQS則會(huì)將當(dāng)前線程以及等待狀態(tài)等信息構(gòu)造成一個(gè)節(jié)點(diǎn)(Node)并將其加入同步隊(duì)列,同時(shí)會(huì)阻塞當(dāng)前線程,當(dāng)同步狀態(tài)釋放時(shí),則會(huì)把節(jié)點(diǎn)中的線程喚醒,使其再次嘗試獲取同步狀態(tài)。

  • AQS主要提供了如下一些方法:

getState():返回同步狀態(tài)的當(dāng)前值;
setState(int newState):設(shè)置當(dāng)前同步狀態(tài);
compareAndSetState(int expect, int update):使用CAS設(shè)置當(dāng)前狀態(tài),該方法能夠保證狀態(tài)設(shè)置的原子性;

tryAcquire(int arg):獨(dú)占式獲取同步狀態(tài),獲取同步狀態(tài)成功后,其他線程需要等待該線程釋放同步狀態(tài)才能獲取同步狀態(tài);
tryRelease(int arg):獨(dú)占式釋放同步狀態(tài);

tryAcquireShared(int arg):共享式獲取同步狀態(tài),返回值大于等于0則表示獲取成功,否則獲取失敗;
tryReleaseShared(int arg):共享式釋放同步狀態(tài);
isHeldExclusively():當(dāng)前同步器是否在獨(dú)占式模式下被線程占用,一般該方法表示是否被當(dāng)前線程所獨(dú)占;
acquire(int arg):獨(dú)占式獲取同步狀態(tài),如果當(dāng)前線程獲取同步狀態(tài)成功,則由該方法返回,否則,將會(huì)進(jìn)入同步隊(duì)列等待,該方法將會(huì)調(diào)用可重寫(xiě)的tryAcquire(int arg)方法;
acquireInterruptibly(int arg):與acquire(int arg)相同,但是該方法響應(yīng)中斷,當(dāng)前線程為獲取到同步狀態(tài)而進(jìn)入到同步隊(duì)列中,如果當(dāng)前線程被中斷,則該方法會(huì)拋出InterruptedException異常并返回;
tryAcquireNanos(int arg,long nanos):超時(shí)獲取同步狀態(tài),如果當(dāng)前線程在nanos時(shí)間內(nèi)沒(méi)有獲取到同步狀態(tài),那么將會(huì)返回false,已經(jīng)獲取則返回true;

acquireShared(int arg):共享式獲取同步狀態(tài),如果當(dāng)前線程未獲取到同步狀態(tài),將會(huì)進(jìn)入同步隊(duì)列等待,與獨(dú)占式的主要區(qū)別是在同一時(shí)刻可以有多個(gè)線程獲取到同步狀態(tài);
acquireSharedInterruptibly(int arg):共享式獲取同步狀態(tài),響應(yīng)中斷;
tryAcquireSharedNanos(int arg, long nanosTimeout):共享式獲取同步狀態(tài),增加超時(shí)限制;
release(int arg):獨(dú)占式釋放同步狀態(tài),該方法會(huì)在釋放同步狀態(tài)之后,將同步隊(duì)列中第一個(gè)節(jié)點(diǎn)包含的線程喚醒;
releaseShared(int arg):共享式釋放同步狀態(tài);

引子

JDK中的并發(fā)鎖工具類(lèi)或多或少都被大家用過(guò)不止一次,比如ReentrantLock,我們知道ReentrantLock的功能是實(shí)現(xiàn)代碼段的并發(fā)訪問(wèn)控制,也就是通常意義上所說(shuō)的鎖,在沒(méi)有看到AbstractQueuedSynchronizer前,可能會(huì)以為它的實(shí)現(xiàn)是通過(guò)類(lèi)似于synchronized,通過(guò)對(duì)對(duì)象加鎖來(lái)實(shí)現(xiàn)的。但事實(shí)上它僅僅是一個(gè)工具類(lèi)!沒(méi)有使用更“高級(jí)”的機(jī)器指令,不是關(guān)鍵字,也不依靠JDK編譯時(shí)的特殊處理,僅僅作為一個(gè)普普通通的類(lèi)就完成了代碼塊的并發(fā)訪問(wèn)控制,這就更讓人疑問(wèn)它怎么實(shí)現(xiàn)的代碼塊的并發(fā)訪問(wèn)控制的了。那就讓我們一起來(lái)仔細(xì)看下Doug Lea怎么去實(shí)現(xiàn)的這個(gè)鎖。

設(shè)計(jì)思想

  • AQS從設(shè)計(jì)之初(即僅單單的從AQS本身來(lái)說(shuō)),它僅僅只是提供獨(dú)占鎖和共享鎖兩種方式,在這里請(qǐng)大家牢記,從AQS本身來(lái)說(shuō),是不存在所謂的公平與非公平性。AQS基于模板模式設(shè)計(jì),因?yàn)閺腁PI的設(shè)計(jì)之初,作者(Doug Lea)已經(jīng)預(yù)訂了任何一個(gè)子類(lèi)只能支持AQS當(dāng)中的獨(dú)占鎖和共享鎖當(dāng)中的一種。所以AQS沒(méi)有抽象方法,所有方法都有默認(rèn)實(shí)現(xiàn),這有區(qū)別于傳統(tǒng)的模板模式。作者這樣做的目的是讓子類(lèi)更加清潔,不需要實(shí)現(xiàn)無(wú)關(guān)的抽象方法。
  • AbstractQueuedSynchronizer繼承自AbstractOwnableSynchronizer。AbstractOwnableSynchronizer簡(jiǎn)稱(chēng)AOS,AOS其實(shí)相對(duì)來(lái)說(shuō)是比較簡(jiǎn)單的,AOS里面只有一個(gè)屬性,那就是exclusiveOwnerThread,也就是用來(lái)標(biāo)識(shí)當(dāng)前占有鎖的線程。另外還有2個(gè)方法,分別用來(lái)get和set這個(gè)exclusiveOwnerThread。
  • 作者為什么需要將持有鎖的線程的標(biāo)識(shí)向上抽取?這是值得我們思考的。在JDK的源碼中有對(duì)AQS這樣的一段描述:

A synchronizer that may be exclusively owned by a thread. This class provides a basis for creating locks and related synchronizers that may entail a notion of ownership. The AbstractOwnableSynchronizer class itself does not manage or use this information. However, subclasses and tools may use appropriately maintained values to help control and monitor access and provide diagnostics.

簡(jiǎn)單翻譯如下:同步器是需要被線程互斥訪問(wèn)的。AOS提供了一個(gè)基本的概念,那就是創(chuàng)建鎖時(shí)賦予一個(gè)對(duì)于這個(gè)鎖的所有權(quán)。AOS本身并不會(huì)去管理或者去使用這些信息。然而子類(lèi)或者其他工具類(lèi)或許會(huì)在適當(dāng)?shù)臅r(shí)候去維護(hù)這些信息用來(lái)控制和監(jiān)聽(tīng)訪問(wèn)控制權(quán)。
AOS源碼如下,為了閱讀方便,我去掉了源碼中的注釋?zhuān)俏覐?qiáng)烈建議你一定要記得去閱讀它,這樣你才能從框架的設(shè)計(jì)者口中得到最準(zhǔn)確的關(guān)于這個(gè)類(lèi)或者接口的設(shè)計(jì)說(shuō)明。

public abstract class AbstractOwnableSynchronizer implements java.io.Serializable {  
    protected AbstractOwnableSynchronizer() { }  
  
    private transient Thread exclusiveOwnerThread;  
  
    protected final void setExclusiveOwnerThread(Thread t) {  
        exclusiveOwnerThread = t;  
    }  
  
    protected final Thread getExclusiveOwnerThread() {  
        return exclusiveOwnerThread;  
    }  
}  

到了這里,我們需要回答一個(gè)問(wèn)題,那就是作者為什么需要將持有鎖的線程的標(biāo)識(shí)向上抽取?其實(shí)原因是很簡(jiǎn)單的。AQS誕生與JDK1.5,而AOS是在JDK1.6才出現(xiàn)的。也就是說(shuō)在整個(gè)AQS的生命過(guò)程中,都沒(méi)有用到AOS中聲明的屬性或方法,這些屬性或方法是在AQS的子類(lèi)中才用到的。也就是說(shuō),在JDK1.6以后,Doug Lea對(duì)AQS的子類(lèi)實(shí)現(xiàn)做出了增強(qiáng)。那么Doug Lea為什么不直接把AOS中聲明的屬性或方法直接放在AQS中?或許Doug Lea認(rèn)為如果這樣做,是對(duì)AQS的一種侵入,因?yàn)锳QS根本不需要這些,所以就往上抽取了一層。

前奏

在深入分析AQS之前,先從AQS的功能上說(shuō)明下AQS,站在使用者的角度,AQS的功能可以分為兩類(lèi):獨(dú)占功能和共享功能,它的所有子類(lèi)中,要么實(shí)現(xiàn)并使用了它獨(dú)占功能的API,要么使用了共享鎖的功能,而不會(huì)同時(shí)使用兩套API,即便是它最有名的子類(lèi)ReentrantReadWriteLock,也是通過(guò)兩個(gè)內(nèi)部類(lèi):讀鎖和寫(xiě)鎖,分別實(shí)現(xiàn)的兩套API來(lái)實(shí)現(xiàn)的,為什么這么做,后面我們?cè)俜治觯侥壳盀橹梗覀冎恍枰靼譇QS在功能上有獨(dú)占控制和共享控制兩種功能即可。

獨(dú)占鎖

對(duì)于ReentrantLock,使用過(guò)的同學(xué)應(yīng)該都知道,通常是這么用它的:

        ReentrantLock reentrantLock = new ReentrantLock();
        reentrantLock.lock();
        try {
            // do something

        }finally {
            reentrantLock.unlock();
        }
    }

ReentrantLock會(huì)保證 do something在同一時(shí)間只有一個(gè)線程在執(zhí)行這段代碼,或者說(shuō),同一時(shí)刻只有一個(gè)線程的lock方法會(huì)返回。其余線程會(huì)被掛起,直到獲取鎖。從這里可以看出,其實(shí)ReentrantLock實(shí)現(xiàn)的就是一個(gè)獨(dú)占鎖的功能:有且只有一個(gè)線程獲取到鎖,其余線程全部掛起,直到該擁有鎖的線程釋放鎖,被掛起的線程被喚醒重新開(kāi)始競(jìng)爭(zhēng)鎖。沒(méi)錯(cuò),ReentrantLock使用的就是AQS的獨(dú)占API實(shí)現(xiàn)的。

那現(xiàn)在我們就從ReentrantLock的實(shí)現(xiàn)開(kāi)始一起看看重入鎖是怎么實(shí)現(xiàn)的。

首先看lock方法:

/**
     * Acquires the lock.
     *
     * <p>Acquires the lock if it is not held by another thread and returns
     * immediately, setting the lock hold count to one.
     *
     * <p>If the current thread already holds the lock then the hold
     * count is incremented by one and the method returns immediately.
     *
     * <p>If the lock is held by another thread then the
     * current thread becomes disabled for thread scheduling
     * purposes and lies dormant until the lock has been acquired,
     * at which time the lock hold count is set to one.
     */
    public void lock() {
        sync.lock();
    }

ReentrantLock內(nèi)部有代理類(lèi)完成具體操作,ReentrantLock只是封裝了統(tǒng)一的一套API而已。值得注意的是,使用過(guò)ReentrantLock的同學(xué)應(yīng)該知道,ReentrantLock又分為公平鎖和非公平鎖,所以,ReentrantLock內(nèi)部只有兩個(gè)sync的實(shí)現(xiàn):

/**
     * Creates an instance of {@code ReentrantLock}.
     * This is equivalent to using {@code ReentrantLock(false)}.
     */
    public ReentrantLock() {
      // 默認(rèn)是非公平鎖,因?yàn)榫S護(hù)公平需要額外的處理成本
        sync = new NonfairSync();
    }

    /**
     * Creates an instance of {@code ReentrantLock} with the
     * given fairness policy.
     *
     * @param fair {@code true} if this lock should use a fair ordering policy
     */
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

公平鎖:每個(gè)線程搶占鎖的順序?yàn)橄群笳{(diào)用lock方法的順序依次獲取鎖,類(lèi)似于排隊(duì)吃飯。

非公平鎖:每個(gè)線程搶占鎖的順序不定,誰(shuí)運(yùn)氣好,誰(shuí)就獲取到鎖,和調(diào)用lock方法的先后順序無(wú)關(guān),類(lèi)似于堵車(chē)時(shí),加塞的那些XXXX。

到這里,通過(guò)ReentrantLock的功能和鎖的所謂排不排隊(duì)的方式,我們是否可以這么猜測(cè)ReentrantLock或者AQS的實(shí)現(xiàn)(現(xiàn)在不清楚誰(shuí)去實(shí)現(xiàn)這些功能):有那么一個(gè)被volatile修飾的標(biāo)志位叫做key,用來(lái)表示有沒(méi)有線程拿走了鎖,或者說(shuō),鎖還存不存在,還需要一個(gè)線程安全的隊(duì)列,維護(hù)一堆被掛起的線程,以至于當(dāng)鎖被歸還時(shí),能通知到這些被掛起的線程,可以來(lái)競(jìng)爭(zhēng)獲取鎖了。

至于公平鎖和非公平鎖,唯一的區(qū)別是在獲取鎖的時(shí)候是直接去獲取鎖,還是進(jìn)入隊(duì)列排隊(duì)的問(wèn)題了。為了驗(yàn)證我們的猜想,我們繼續(xù)看一下ReentrantLock中公平鎖的實(shí)現(xiàn):

 /**
     * Sync object for fair locks
     */
    static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;

        final void lock() {
            acquire(1);
        }

        /**
         * Fair version of tryAcquire.  Don't grant access unless
         * recursive call or no waiters or is first.
         */
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
    }

調(diào)用到了AQS的acquire方法:

/**
     * Acquires in exclusive mode, ignoring interrupts.  Implemented
     * by invoking at least once {@link #tryAcquire},
     * returning on success.  Otherwise the thread is queued, possibly
     * repeatedly blocking and unblocking, invoking {@link
     * #tryAcquire} until success.  This method can be used
     * to implement method {@link Lock#lock}.
     *
     */
    public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

從方法名字上看語(yǔ)義是,嘗試獲取鎖,獲取不到則創(chuàng)建一個(gè)waiter(當(dāng)前線程)后放到隊(duì)列中,這和我們猜測(cè)的好像很類(lèi)似.
看下tryAcquire方法:

protected boolean tryAcquire(int arg) {
        throw new UnsupportedOperationException();
    }

留空了,Doug Lea是想留給子類(lèi)去實(shí)現(xiàn)(既然要給子類(lèi)實(shí)現(xiàn),應(yīng)該用抽象方法,但是Doug Lea沒(méi)有這么做,原因是AQS有兩種功能,面向兩種使用場(chǎng)景,需要給子類(lèi)定義的方法都是抽象方法了,會(huì)導(dǎo)致子類(lèi)無(wú)論如何都需要實(shí)現(xiàn)另外一種場(chǎng)景的抽象方法,顯然,這對(duì)子類(lèi)來(lái)說(shuō)是不友好的。)

看下FairSync的tryAcquire方法:

/**
         * Fair version of tryAcquire.  Don't grant access unless
         * recursive call or no waiters or is first.
         */
        protected final boolean tryAcquire(int acquires) {
            // 當(dāng)前線程
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

getState方法是AQS的方法,因?yàn)樵贏QS里面有個(gè)叫statede的標(biāo)志位 :

/**
     * The synchronization state.
     */
    private volatile int state;

    /**
     * Returns the current value of synchronization state.
     * This operation has memory semantics of a {@code volatile} read.
     * @return current state value
     */
    protected final int getState() {
        return state;
    }

事實(shí)上,這個(gè)state就是前面我們猜想的那個(gè)“key”!

回到tryAcquire方法:

protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();//獲取當(dāng)前線程
            int c = getState();  //獲取父類(lèi)AQS中的標(biāo)志位
            if (c == 0) {
                if (!hasQueuedPredecessors() && 
                    //如果隊(duì)列中沒(méi)有其他線程  說(shuō)明沒(méi)有線程正在占有鎖!
                    compareAndSetState(0, acquires)) { 
                    //修改一下?tīng)顟B(tài)位,注意:這里的acquires是在lock的時(shí)候傳遞來(lái)的,從上面的圖中可以知道,這個(gè)值是寫(xiě)死的1
                    setExclusiveOwnerThread(current);
                    //如果通過(guò)CAS操作將狀態(tài)為更新成功則代表當(dāng)前線程獲取鎖,因此,將當(dāng)前線程設(shè)置到AQS的一個(gè)變量中,說(shuō)明這個(gè)線程拿走了鎖。
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
             //如果不為0 意味著,鎖已經(jīng)被拿走了,但是,因?yàn)镽eentrantLock是重入鎖,
             //是可以重復(fù)lock,unlock的,只要成對(duì)出現(xiàn)行。一次。這里還要再判斷一次 獲取鎖的線程是不是當(dāng)前請(qǐng)求鎖的線程。
                int nextc = c + acquires;//如果是的,累加在state字段上就可以了。
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

到此,如果如果獲取鎖,tryAcquire返回true,反之,返回false,回到AQS的acquire方法。

如果沒(méi)有獲取到鎖,按照我們的描述,應(yīng)該將當(dāng)前線程放到隊(duì)列中去,只不過(guò),在放之前,需要做些包裝。

先看addWaiter方法:

public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
private Node addWaiter(Node mode) {
        Node node = new Node(Thread.currentThread(), mode);
        // Try the fast path of enq; backup to full enq on failure
        Node pred = tail;
        if (pred != null) {
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        enq(node);
        return node;
    }

用當(dāng)前線程去構(gòu)造一個(gè)Node對(duì)象,mode是一個(gè)表示Node類(lèi)型的字段,僅僅表示這個(gè)節(jié)點(diǎn)是獨(dú)占的,還是共享的,或者說(shuō),AQS的這個(gè)隊(duì)列中,哪些節(jié)點(diǎn)是獨(dú)占的,哪些是共享的。

這里lock調(diào)用的是AQS獨(dú)占的API,當(dāng)然,可以寫(xiě)死是獨(dú)占狀態(tài)的節(jié)點(diǎn)。

創(chuàng)建好節(jié)點(diǎn)后,將節(jié)點(diǎn)加入到隊(duì)列尾部,此處,在隊(duì)列不為空的時(shí)候,先嘗試通過(guò)cas方式修改尾節(jié)點(diǎn)為最新的節(jié)點(diǎn),如果修改失敗,意味著有并發(fā),這個(gè)時(shí)候才會(huì)進(jìn)入enq中死循環(huán),“自旋”方式修改。

將線程的節(jié)點(diǎn)接入到隊(duì)里中后,當(dāng)然還需要做一件事:將當(dāng)前線程掛起!這個(gè)事,由acquireQueued來(lái)做.

在解釋acquireQueued之前,我們需要先看下AQS中隊(duì)列的內(nèi)存結(jié)構(gòu),我們知道,隊(duì)列由Node類(lèi)型的節(jié)點(diǎn)組成,其中至少有兩個(gè)變量,一個(gè)封裝線程,一個(gè)封裝節(jié)點(diǎn)類(lèi)型。

而實(shí)際上,它的內(nèi)存結(jié)構(gòu)是這樣的(第一次節(jié)點(diǎn)插入時(shí),第一個(gè)節(jié)點(diǎn)是一個(gè)空節(jié)點(diǎn),如果不為空,則表示有一個(gè)線程已經(jīng)獲取鎖。事實(shí)上,隊(duì)列的第一個(gè)節(jié)點(diǎn)就是代表持有鎖的節(jié)點(diǎn)):



黃色節(jié)點(diǎn)為隊(duì)列默認(rèn)的頭節(jié)點(diǎn),每次有線程競(jìng)爭(zhēng)失敗,進(jìn)入隊(duì)列后其實(shí)都是插入到隊(duì)列的尾節(jié)點(diǎn)(tail后面)后面。這個(gè)從enq方法可以看出來(lái),上文中有提到enq方法為將節(jié)點(diǎn)插入隊(duì)列的方法:

/**
     * Inserts node into queue, initializing if necessary. See picture above.
     * @param node the node to insert
     * @return node's predecessor
     */
    private Node enq(final Node node) {
        for (;;) {
            Node t = tail;
            if (t == null) { // Must initialize
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }

/**
     * CAS tail field. Used only by enq.
     */
    private final boolean compareAndSetTail(Node expect, Node update) {
        return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
    }
// 用到了unsafe 操作:
/**
     * Setup to support compareAndSet. We need to natively implement
     * this here: For the sake of permitting future enhancements, we
     * cannot explicitly subclass AtomicInteger, which would be
     * efficient and useful otherwise. So, as the lesser of evils, we
     * natively implement using hotspot intrinsics API. And while we
     * are at it, we do the same for other CASable fields (which could
     * otherwise be done with atomic field updaters).
     */
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long stateOffset;
    private static final long headOffset;
    private static final long tailOffset;
    private static final long waitStatusOffset;
    private static final long nextOffset;

    static {
        try {
            stateOffset = unsafe.objectFieldOffset
                (AbstractQueuedSynchronizer.class.getDeclaredField("state"));
            headOffset = unsafe.objectFieldOffset
                (AbstractQueuedSynchronizer.class.getDeclaredField("head"));
            tailOffset = unsafe.objectFieldOffset
                (AbstractQueuedSynchronizer.class.getDeclaredField("tail"));
            waitStatusOffset = unsafe.objectFieldOffset
                (Node.class.getDeclaredField("waitStatus"));
            nextOffset = unsafe.objectFieldOffset
                (Node.class.getDeclaredField("next"));

        } catch (Exception ex) { throw new Error(ex); }
    }

再回來(lái)看看

* Acquires in exclusive uninterruptible mode for thread already in
     * queue. Used by condition wait methods as well as acquire.
     *
final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
             //如果當(dāng)前的節(jié)點(diǎn)是head說(shuō)明他是隊(duì)列中第一個(gè)“有效的”節(jié)點(diǎn),因此嘗試獲取,上文中有提到這個(gè)類(lèi)是交給子類(lèi)去擴(kuò)展的。
                    setHead(node);//成功后,將上圖中的黃色節(jié)點(diǎn)移除,Node1變成頭節(jié)點(diǎn)。
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node) && 
                //否則,檢查前一個(gè)節(jié)點(diǎn)的狀態(tài)為,看當(dāng)前獲取鎖失敗的線程是否需要掛起。
                    parkAndCheckInterrupt()) 
               //如果需要,借助JUC包下的LockSopport類(lèi)的靜態(tài)方法Park掛起當(dāng)前線程,直到被喚醒。
                    interrupted = true;
            }
        } finally {
            if (failed) //如果有異常
                cancelAcquire(node);// 取消請(qǐng)求,對(duì)應(yīng)到隊(duì)列操作,就是將當(dāng)前節(jié)點(diǎn)從隊(duì)列中移除。
        }
    }

 /**
     * Checks and updates status for a node that failed to acquire.
     * Returns true if thread should block. This is the main signal
     * control in all acquire loops.  Requires that pred == node.prev.
     *
     * @param pred node's predecessor holding status
     * @param node the node
     * @return {@code true} if thread should block
     */
    private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        int ws = pred.waitStatus;
        if (ws == Node.SIGNAL)
            /*
             * This node has already set status asking a release
             * to signal it, so it can safely park.
             */
            return true;
        if (ws > 0) {
            /*
             * Predecessor was cancelled. Skip over predecessors and
             * indicate retry.
             */
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
            /*
             * waitStatus must be 0 or PROPAGATE.  Indicate that we
             * need a signal, but don't park yet.  Caller will need to
             * retry to make sure it cannot acquire before parking.
             */
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }

/**
     * Convenience method to park and then check if interrupted
     *
     * @return {@code true} if interrupted
     */
    private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this);
        return Thread.interrupted();
    }

這塊代碼有幾點(diǎn)需要說(shuō)明:

1. Node節(jié)點(diǎn)中,除了存儲(chǔ)當(dāng)前線程,節(jié)點(diǎn)類(lèi)型,隊(duì)列中前后元素的變量,還有一個(gè)叫waitStatus的變量,改變量用于描述節(jié)點(diǎn)的狀態(tài),為什么需要這個(gè)狀態(tài)呢?

原因是:AQS的隊(duì)列中,在有并發(fā)時(shí),肯定會(huì)存取一定數(shù)量的節(jié)點(diǎn),每個(gè)節(jié)點(diǎn)代表了一個(gè)線程的狀態(tài),有的線程可能“等不及”獲取鎖了,需要放棄競(jìng)爭(zhēng),退出隊(duì)列,有的線程在等待一些條件滿(mǎn)足,滿(mǎn)足后才恢復(fù)執(zhí)行(這里的描述很像某個(gè)J.U.C包下的工具類(lèi),ReentrankLock的Condition,事實(shí)上,Condition同樣也是AQS的子類(lèi))等等,總之,各個(gè)線程有各個(gè)線程的狀態(tài),但總需要一個(gè)變量來(lái)描述它,這個(gè)變量就叫waitStatus,它有四種狀態(tài):

static final class Node {
        /** Marker to indicate a node is waiting in shared mode */
        static final Node SHARED = new Node();
        /** Marker to indicate a node is waiting in exclusive mode */
        static final Node EXCLUSIVE = null;

        /** waitStatus value to indicate thread has cancelled */
        static final int CANCELLED =  1;
        /** waitStatus value to indicate successor's thread needs unparking */
        static final int SIGNAL    = -1;
        /** waitStatus value to indicate thread is waiting on condition */
        static final int CONDITION = -2;
        /**
         * waitStatus value to indicate the next acquireShared should
         * unconditionally propagate
         */
        static final int PROPAGATE = -3;

        /**
         * Status field, taking on only the values:
         *   SIGNAL:     The successor of this node is (or will soon be)
         *               blocked (via park), so the current node must
         *               unpark its successor when it releases or
         *               cancels. To avoid races, acquire methods must
         *               first indicate they need a signal,
         *               then retry the atomic acquire, and then,
         *               on failure, block.
         *   CANCELLED:  This node is cancelled due to timeout or interrupt.
         *               Nodes never leave this state. In particular,
         *               a thread with cancelled node never again blocks.
         *   CONDITION:  This node is currently on a condition queue.
         *               It will not be used as a sync queue node
         *               until transferred, at which time the status
         *               will be set to 0. (Use of this value here has
         *               nothing to do with the other uses of the
         *               field, but simplifies mechanics.)
         *   PROPAGATE:  A releaseShared should be propagated to other
         *               nodes. This is set (for head node only) in
         *               doReleaseShared to ensure propagation
         *               continues, even if other operations have
         *               since intervened.
         *   0:          None of the above
         *
         * The values are arranged numerically to simplify use.
         * Non-negative values mean that a node doesn't need to
         * signal. So, most code doesn't need to check for particular
         * values, just for sign.
         *
         * The field is initialized to 0 for normal sync nodes, and
         * CONDITION for condition nodes.  It is modified using CAS
         * (or when possible, unconditional volatile writes).
         */
        volatile int waitStatus;

        
        volatile Node prev;

        volatile Node next;

        volatile Thread thread;

        Node nextWaiter;

      // 節(jié)點(diǎn)含有等待隊(duì)列的頭尾指針
 /**
     * Head of the wait queue, lazily initialized.  Except for
     * initialization, it is modified only via method setHead.  Note:
     * If head exists, its waitStatus is guaranteed not to be
     * CANCELLED.
     */
    private transient volatile Node head;

    /**
     * Tail of the wait queue, lazily initialized.  Modified only via
     * method enq to add new wait node.
     */
    private transient volatile Node tail;

    /**
     * The synchronization state.
     */·
    private volatile int state;

分別表示:

  • 節(jié)點(diǎn)取消 CANCELLED
  • 節(jié)點(diǎn)等待觸發(fā) SIGNAL
  • 節(jié)點(diǎn)等待條件 CONDITION
  • 節(jié)點(diǎn)狀態(tài)需要向后傳播。 PROPAGATE
    只有當(dāng)前節(jié)點(diǎn)的前一個(gè)節(jié)點(diǎn)為SIGNAL時(shí),才能當(dāng)前節(jié)點(diǎn)才能被掛起。
  1. 對(duì)線程的掛起及喚醒操作是通過(guò)使用UNSAFE類(lèi)調(diào)用JNI方法實(shí)現(xiàn)的。當(dāng)然,還提供了掛起指定時(shí)間后喚醒的API,在后面我們會(huì)講到。

到此為止,一個(gè)線程對(duì)于鎖的一次競(jìng)爭(zhēng)才告于段落,結(jié)果有兩種,要么成功獲取到鎖(不用進(jìn)入到AQS隊(duì)列中),要么,獲取失敗,被掛起,等待下次喚醒后繼續(xù)循環(huán)嘗試獲取鎖,值得注意的是,AQS的隊(duì)列為FIFO隊(duì)列,所以,每次被CPU假喚醒,且當(dāng)前線程不是出在頭節(jié)點(diǎn)的位置,也是會(huì)被掛起的。AQS通過(guò)這樣的方式,實(shí)現(xiàn)了競(jìng)爭(zhēng)的排隊(duì)策略。

看完了獲取鎖,在看看釋放鎖,具體看代碼之前,我們可以先繼續(xù)猜下,釋放操作需要做哪些事情:

因?yàn)楂@取鎖的線程的節(jié)點(diǎn),此時(shí)在AQS的頭節(jié)點(diǎn)位置,所以,可能需要將頭節(jié)點(diǎn)移除。

而應(yīng)該是直接釋放鎖,然后找到AQS的頭節(jié)點(diǎn),通知它可以來(lái)競(jìng)爭(zhēng)鎖了。
是不是這樣呢?我們繼續(xù)來(lái)看下,同樣我們用ReentrantLock的FairSync來(lái)說(shuō)明

/**
     * Attempts to release this lock.
     *
     * <p>If the current thread is the holder of this lock then the hold
     * count is decremented.  If the hold count is now zero then the lock
     * is released.  If the current thread is not the holder of this
     * lock then {@link IllegalMonitorStateException} is thrown.
     *
     * @throws IllegalMonitorStateException if the current thread does not
     *         hold this lock
     */
    public void unlock() {
        sync.release(1);
    }

/**
     * Releases in exclusive mode.  Implemented by unblocking one or
     * more threads if {@link #tryRelease} returns true.
     * This method can be used to implement method {@link Lock#unlock}.
     *
     * @param arg the release argument.  This value is conveyed to
     *        {@link #tryRelease} but is otherwise uninterpreted and
     *        can represent anything you like.
     * @return the value returned from {@link #tryRelease}
     */
    public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

unlock方法調(diào)用了AQS的release方法,同樣傳入了參數(shù)1,和獲取鎖的相應(yīng)對(duì)應(yīng),獲取一個(gè)鎖,標(biāo)示為+1,釋放一個(gè)鎖,標(biāo)志位-1。

同樣,release為空方法,子類(lèi)自己實(shí)現(xiàn)邏輯:


        protected final boolean tryRelease(int releases) {
            int c = getState() - releases;
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }

釋放鎖,成功后,找到AQS的頭節(jié)點(diǎn),并喚醒它即可:


/**
     * Wakes up node's successor, if one exists.
     *
     * @param node the node
     */
    private void unparkSuccessor(Node node) {
        /*
         * If status is negative (i.e., possibly needing signal) try
         * to clear in anticipation of signalling.  It is OK if this
         * fails or if status is changed by waiting thread.
         */
        int ws = node.waitStatus;
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);

        /*
         * Thread to unpark is held in successor, which is normally
         * just the next node.  But if cancelled or apparently null,
         * traverse backwards from tail to find the actual
         * non-cancelled successor.
         */
        Node s = node.next;
        if (s == null || s.waitStatus > 0) {
            s = null;
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)
                    s = t;
        }
        if (s != null)
            LockSupport.unpark(s.thread);
    }

值得注意的是,尋找的順序是從隊(duì)列尾部開(kāi)始往前去找的最前面的一個(gè)waitStatus小于0的節(jié)點(diǎn)。

到此,ReentrantLock的lock和unlock方法已經(jīng)基本解析完畢了,唯獨(dú)還剩下一個(gè)非公平鎖NonfairSync沒(méi)說(shuō),其實(shí),它和公平鎖的唯一區(qū)別就是獲取鎖的方式不同,一個(gè)是按前后順序一次獲取鎖,一個(gè)是搶占式的獲取鎖,那ReentrantLock是怎么實(shí)現(xiàn)的呢?再看兩段代碼:

/**
     * Sync object for non-fair locks
     */
    static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;

        /**
         * Performs lock.  Try immediate barge, backing up to normal
         * acquire on failure.
         */
        final void lock() {
            // 非公平鎖 直接進(jìn)行一次搶的計(jì)算
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                // 沒(méi)搶到 則走隊(duì)列
                acquire(1);
        }

        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }

/**
         * Performs non-fair tryLock.  tryAcquire is implemented in
         * subclasses, but both need nonfair try for trylock method.
         */
        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

小結(jié)

總的來(lái)說(shuō),思路其實(shí)并不復(fù)雜,還是使用的標(biāo)志位+隊(duì)列的方式,記錄獲取鎖、競(jìng)爭(zhēng)鎖、釋放鎖等一系列鎖的狀態(tài),或許用更準(zhǔn)確一點(diǎn)的描述的話,應(yīng)該是使用的標(biāo)志位+隊(duì)列的方式,記錄鎖、競(jìng)爭(zhēng)、釋放等一系列獨(dú)占的狀態(tài),因?yàn)檎驹贏QS的層面state可以表示鎖,也可以表示其他狀態(tài),它并不關(guān)心它的子類(lèi)把它變成一個(gè)什么工具類(lèi),而只是提供了一套維護(hù)一個(gè)獨(dú)占狀態(tài)。甚至,最準(zhǔn)確的是AQS只是維護(hù)了一個(gè)狀態(tài),因?yàn)椋瑒e忘了,它還有一套共享狀態(tài)的API,所以,AQS只是維護(hù)一個(gè)狀態(tài),一個(gè)控制各個(gè)線程何時(shí)可以訪問(wèn)的狀態(tài),它只對(duì)狀態(tài)負(fù)責(zé),而這個(gè)狀態(tài)表示什么含義,由子類(lèi)自己去定義。

AbstractQueuedSynchronizer暴露的API

分三個(gè)方面:

  • 一:獨(dú)占鎖 與共享鎖
  • 二:不響應(yīng)中斷 與 響應(yīng)中斷
  • 三:aquire 與 release

我們知道,AQS僅僅只是提供獨(dú)占鎖和共享鎖兩種方式,但是每種方式都有響應(yīng)中斷和不響應(yīng)中斷的區(qū)別,所以說(shuō)AQS鎖的更細(xì)粒度的劃分為:

  • 不響應(yīng)中斷的獨(dú)占鎖(acquire)
  • 響應(yīng)中斷的獨(dú)占鎖(acquireInterruptibly)
  • 不響應(yīng)中斷的共享鎖(acquireShared)
  • 響應(yīng)中斷的共享鎖(acquireSharedInterruptibly)

這四種方式在AQS中的入口在上面已經(jīng)標(biāo)注,而釋放鎖的方式只有兩種,獨(dú)占鎖的釋放與共享鎖的釋放。分別為:

  • 獨(dú)占鎖的釋放(release)
  • 共享鎖的釋放(releaseShared)

因?yàn)锳QS是基于模板模式的設(shè)計(jì),可想而知,上面的方法,子類(lèi)不應(yīng)該去覆蓋,因?yàn)檫@些方法定義了整體流程,事實(shí)上作者也阻止你去覆蓋它,因?yàn)檫@些方法都是final的。

在上面所有的方法中,都調(diào)用了與之相對(duì)應(yīng)的try方法。在這里需要注意的一點(diǎn)是,acquire和acquireInterruptibly在AQS中調(diào)用的是同一個(gè)try方法,acquireShared和acquireSharedInterruptibly也是調(diào)用相同的try方法,并且try方法在AQS中都提供了空實(shí)現(xiàn)。

也就是說(shuō),作者暗示著子類(lèi)應(yīng)該去重寫(xiě)這些try方法,至于如何去重寫(xiě)try方法,完全是子類(lèi)的自由。

例如: ReentrantLock是一個(gè)典型的獨(dú)占鎖,它提供了對(duì)try方法的實(shí)現(xiàn),并且提供了兩種實(shí)現(xiàn)方式。這兩種不同的try方式,就衍生出了公平與非公平的概念。即ReentrantLock提供如下:

  • 非公平方式的不響應(yīng)中斷的獨(dú)占鎖
  • 非公平方式的響應(yīng)中斷的獨(dú)占鎖
  • 公平方式的不響應(yīng)中斷的獨(dú)占鎖
  • 公平方式的響應(yīng)中斷的獨(dú)占鎖

CLH 存儲(chǔ)線程隊(duì)列 + 競(jìng)爭(zhēng)資源的Status

AQS的基本數(shù)據(jù)結(jié)構(gòu)為Node,關(guān)于Node,JDK作者寫(xiě)了詳細(xì)的注釋?zhuān)@里我大致總結(jié)幾點(diǎn):

  • 1.1 AbstractQueuedSynchronizer的等待隊(duì)列是CLH隊(duì)列的變種,CLH隊(duì)列通常用于自旋鎖,AQS中的CLH可以簡(jiǎn)單的理解為“等待鎖的線程隊(duì)列”。
  • 1.2 每個(gè)節(jié)點(diǎn)中持有一個(gè)名為"status"的字段用于一條線程是否應(yīng)當(dāng)阻塞的追蹤。
  • 1.3 一條線程所在節(jié)點(diǎn)如果它處于隊(duì)列頭的下一個(gè)節(jié)點(diǎn),那么它會(huì)嘗試去acquire。因?yàn)轭^節(jié)點(diǎn)是一個(gè)dummy(虛擬的、假的)節(jié)點(diǎn),也就是說(shuō)頭節(jié)點(diǎn)不持有任何線程。所以,一條線程所在節(jié)點(diǎn)如果它處于隊(duì)列頭節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn),那么他會(huì)嘗試去acquire,但是并不保證成功。
  • 1.4 要進(jìn)入隊(duì)列,你只需要自動(dòng)將它拼接在隊(duì)列尾部即可;如果獲取了鎖,你只需要設(shè)置header字段即可。

CLH鎖

CLH(Craig, Landin, and Hagersten locks): 是一個(gè)自旋鎖,能確保無(wú)饑餓性,提供先來(lái)先服務(wù)的公平性。
CLH鎖也是一種基于鏈表的可擴(kuò)展、高性能、公平的自旋鎖,申請(qǐng)線程只在本地變量上自旋,它不斷輪詢(xún)前驅(qū)的狀態(tài),如果發(fā)現(xiàn)前驅(qū)釋放了鎖就結(jié)束自旋。

  • CLH隊(duì)列中的結(jié)點(diǎn)QNode中含有一個(gè)locked字段,該字段若為true表示該線程需要獲取鎖,且不釋放鎖,為false表示線程釋放了鎖。結(jié)點(diǎn)之間是通過(guò)隱形的鏈表相連,之所以叫隱形的鏈表是因?yàn)檫@些結(jié)點(diǎn)之間沒(méi)有明顯的next指針,而是通過(guò)myPred所指向的結(jié)點(diǎn)的變化情況來(lái)影響myNode的行為。CLHLock上還有一個(gè)尾指針,始終指向隊(duì)列的最后一個(gè)結(jié)點(diǎn)。CLHLock的類(lèi)圖如下所示:

當(dāng)一個(gè)線程需要獲取鎖時(shí):
1.創(chuàng)建一個(gè)的QNode,將其中的locked設(shè)置為true表示需要獲取鎖
2.線程對(duì)tail域調(diào)用getAndSet方法,使自己成為隊(duì)列的尾部,同時(shí)獲取一個(gè)指向其前趨結(jié)點(diǎn)的引用myPred
3.該線程就在前趨結(jié)點(diǎn)的locked字段上旋轉(zhuǎn),直到前趨結(jié)點(diǎn)釋放鎖
4.當(dāng)一個(gè)線程需要釋放鎖時(shí),將當(dāng)前結(jié)點(diǎn)的locked域設(shè)置為false,同時(shí)回收前趨結(jié)點(diǎn)

如下圖,線程A需要獲取鎖,其myNode域?yàn)閠rue,tail指向線程A的結(jié)點(diǎn),然后線程B也加入到線程A后面,tail指向線程B的結(jié)點(diǎn)。然后線程A和B都在其myPred域上旋轉(zhuǎn),一旦它的myPred結(jié)點(diǎn)的locked字段變?yōu)閒alse,它就可以獲取鎖。明顯線程A的myPred locked域?yàn)閒alse,此時(shí)線程A獲取到了鎖。

整個(gè)CLH的代碼如下,其中用到了ThreadLocal類(lèi),將QNode綁定到每一個(gè)線程上,同時(shí)用到了AtomicReference,對(duì)尾指針的修改正是調(diào)用它的getAndSet()操作來(lái)實(shí)現(xiàn)的,它能夠保證以原子方式更新對(duì)象引用。

public class CLHLock {  
    
    AtomicReference<QNode> tail = new AtomicReference<QNode>(new QNode());  
    ThreadLocal<QNode> myPred;  
    ThreadLocal<QNode> myNode;  
  
    public static class QNode {
        //注意這個(gè)地方 如果不加volatile則會(huì)導(dǎo)致線程永遠(yuǎn)死循環(huán)
        //關(guān)于volatile的用法在我的另外一篇文章 http://www.cnblogs.com/daxin/p/3364014.html
        public volatile boolean locked = false;
    }
    
    public CLHLock() {  
        myNode = new ThreadLocal<QNode>() {  
            protected QNode initialValue() {  
                return new QNode();  
            }  
        };  
        myPred = new ThreadLocal<QNode>() {  
            protected QNode initialValue() {  
                return null;  
            }  
        };  
    }  
  
    public void lock() {  
        QNode qnode = myNode.get();  
        qnode.locked = true;  
        QNode pred = tail.getAndSet(qnode);  
        myPred.set(pred);  
        while (pred.locked) {  
            //非阻塞算法
        }  
    }  
  
    public void unlock() {  
        QNode qnode = myNode.get();  
        qnode.locked = false;  
        myNode.set(myPred.get());  
    }  
}

CLH同步隊(duì)列

在CLH同步隊(duì)列中,一個(gè)節(jié)點(diǎn)表示一個(gè)線程,它保存著線程的引用(thread)、狀態(tài)(waitStatus)、前驅(qū)節(jié)點(diǎn)(prev)、后繼節(jié)點(diǎn)(next)
CLH同步隊(duì)列結(jié)構(gòu)圖如下:

  • 入列

學(xué)了數(shù)據(jù)結(jié)構(gòu)的我們,CLH隊(duì)列入列是再簡(jiǎn)單不過(guò)了,無(wú)非就是tail指向新節(jié)點(diǎn)、新節(jié)點(diǎn)的prev指向當(dāng)前最后的節(jié)點(diǎn),當(dāng)前最后一個(gè)節(jié)點(diǎn)的next指向當(dāng)前節(jié)點(diǎn)。代碼我們可以看看addWaiter(Node node)方法:

  • 出列

CLH同步隊(duì)列遵循FIFO,首節(jié)點(diǎn)的線程釋放同步狀態(tài)后,將會(huì)喚醒它的后繼節(jié)點(diǎn)(next),而后繼節(jié)點(diǎn)將會(huì)在獲取同步狀態(tài)成功時(shí)將自己設(shè)置為首節(jié)點(diǎn),這個(gè)過(guò)程非常簡(jiǎn)單,head執(zhí)行該節(jié)點(diǎn)并斷開(kāi)原首節(jié)點(diǎn)的next和當(dāng)前節(jié)點(diǎn)的prev即可,注意在這個(gè)過(guò)程是不需要使用CAS來(lái)保證的,因?yàn)橹挥幸粋€(gè)線程能夠成功獲取到同步狀態(tài)。

  • 整體示意圖

AQS共享功能的實(shí)現(xiàn)

以CountDownLatch為例,CountDownLatch常被用在多線程環(huán)境下,它在初始時(shí)需要指定一個(gè)計(jì)數(shù)器的大小,然后可被多個(gè)線程并發(fā)的實(shí)現(xiàn)減1操作,并在計(jì)數(shù)器為0后調(diào)用await方法的線程被喚醒,從而實(shí)現(xiàn)多線程間的協(xié)作。它在多線程環(huán)境下的基本使用方式為:D Lea在源碼注釋里已經(jīng)給了兩個(gè)經(jīng)典的應(yīng)用例子:

 * <p><b>Sample usage:</b> Here is a pair of classes in which a group
 * of worker threads use two countdown latches:
 * <ul>
 * <li>The first is a start signal that prevents any worker from proceeding
 * until the driver is ready for them to proceed;
 * <li>The second is a completion signal that allows the driver to wait
 * until all workers have completed.
 * </ul>
 *
 *  <pre> {@code
 * class Driver { // ...
 *   void main() throws InterruptedException {
 *     CountDownLatch startSignal = new CountDownLatch(1);
 *     CountDownLatch doneSignal = new CountDownLatch(N);
 *
 *     for (int i = 0; i < N; ++i) // create and start threads
 *       new Thread(new Worker(startSignal, doneSignal)).start();
 *
 *     doSomethingElse();            // don't let run yet
 *     startSignal.countDown();      // let all threads proceed
 *     doSomethingElse();
 *     doneSignal.await();           // wait for all to finish
 *   }
 * }
 *
 * class Worker implements Runnable {
 *   private final CountDownLatch startSignal;
 *   private final CountDownLatch doneSignal;
 *   Worker(CountDownLatch startSignal, CountDownLatch doneSignal) {
 *     this.startSignal = startSignal;
 *     this.doneSignal = doneSignal;
 *   }
 *   public void run() {
 *     try {
 *       startSignal.await();
 *       doWork();
 *       doneSignal.countDown();
 *     } catch (InterruptedException ex) {} // return;
 *   }
 *
 *   void doWork() { ... }
 * }}</pre>
 *
 * <p>Another typical usage would be to divide a problem into N parts,
 * describe each part with a Runnable that executes that portion and
 * counts down on the latch, and queue all the Runnables to an
 * Executor.  When all sub-parts are complete, the coordinating thread
 * will be able to pass through await. (When threads must repeatedly
 * count down in this way, instead use a {@link CyclicBarrier}.)
 *
 *  <pre> {@code
 * class Driver2 { // ...
 *   void main() throws InterruptedException {
 *     CountDownLatch doneSignal = new CountDownLatch(N);
 *     Executor e = ...
 *
 *     for (int i = 0; i < N; ++i) // create and start threads
 *       e.execute(new WorkerRunnable(doneSignal, i));
 *
 *     doneSignal.await();           // wait for all to finish
 *   }
 * }
 *
 * class WorkerRunnable implements Runnable {
 *   private final CountDownLatch doneSignal;
 *   private final int i;
 *   WorkerRunnable(CountDownLatch doneSignal, int i) {
 *     this.doneSignal = doneSignal;
 *     this.i = i;
 *   }
 *   public void run() {
 *     try {
 *       doWork(i);
 *       doneSignal.countDown();
 *     } catch (InterruptedException ex) {} // return;
 *   }
 *
 *   void doWork() { ... }
 * }}</pre>

再看一下CountDownlatch源碼,代碼不多

public class CountDownLatch {
    /**
     * Synchronization control For CountDownLatch.
     * Uses AQS state to represent count.
     */
    // 我們可以繼承AbstractQueuedSynchronizer來(lái)實(shí)現(xiàn)自己線程鎖
    private static final class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = 4982264981922014374L;

        Sync(int count) { 
            // 重入鎖里是從1開(kāi)始增或減,這里直接設(shè)置計(jì)數(shù)!
            setState(count);
        }

        int getCount() {
            return getState();
        }

        protected int tryAcquireShared(int acquires) {
            // 獲取共享鎖狀態(tài)
            return (getState() == 0) ? 1 : -1;
        }

        protected boolean tryReleaseShared(int releases) {
            // Decrement count; signal when transition to zero
            for (;;) {
                int c = getState();
                if (c == 0)
                    return false;
                int nextc = c-1;
                if (compareAndSetState(c, nextc))
                    return nextc == 0;
            }
        }
    }

    private final Sync sync;
     
    // 直接構(gòu)造了帶計(jì)數(shù)的共享標(biāo)志sync
    public CountDownLatch(int count) {
        if (count < 0) throw new IllegalArgumentException("count < 0");
        this.sync = new Sync(count);
    }

    // 直接調(diào)用了AQS的acquireSharedInterruptibly
    public void await() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }

   
    public boolean await(long timeout, TimeUnit unit)
        throws InterruptedException {
        return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
    }

    // 計(jì)數(shù)標(biāo)志 減少
    public void countDown() {
        sync.releaseShared(1);
    }

  
    public long getCount() {
        return sync.getCount();
    }

    public String toString() {
        return super.toString() + "[Count = " + sync.getCount() + "]";
    }
}

讓多線程等待

public void await() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }

public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        // 支持線程中斷
        if (Thread.interrupted())
            throw new InterruptedException();
        if (tryAcquireShared(arg) < 0)
            doAcquireSharedInterruptibly(arg);
    }

從方法名上看,這個(gè)方法的調(diào)用是響應(yīng)線程的打斷的,所以在前兩行會(huì)檢查下線程是否被打斷。接著,嘗試著獲取共享鎖,小于0,表示獲取失敗,通過(guò)本系列的上半部分的解讀, 我們知道AQS在獲取鎖的思路是,先嘗試直接獲取鎖,如果失敗會(huì)將當(dāng)前線程放在隊(duì)列中,按照FIFO的原則等待鎖。而對(duì)于共享鎖也是這個(gè)思路,如果和獨(dú)占鎖一致,這里的tryAcquireShared應(yīng)該是個(gè)空方法,留給子類(lèi)去判斷:
再看看CountDownLatch:

protected int tryAcquireShared(int acquires) {
            // 0的話標(biāo)志鎖可以獲取,已經(jīng)減為0了,否則不成功
            return (getState() == 0) ? 1 : -1;
        }

如果state變成0了,則返回1,表示獲取成功,否則返回-1則表示獲取失敗。

看到這里,讀者可能會(huì)發(fā)現(xiàn), await方法的獲取方式更像是在獲取一個(gè)獨(dú)占鎖,那為什么這里還會(huì)用tryAcquireShared呢?

CountDownLatch的await方法是不是只能在主線程中調(diào)用?
答案是否定的,CountDownLatch的await方法可以在多個(gè)線程中調(diào)用

當(dāng)CountDownLatch的計(jì)數(shù)器為0后,調(diào)用await的方法都會(huì)依次返回。 也就是說(shuō)可以多個(gè)線程同時(shí)在等待await方法返回,所以它被設(shè)計(jì)成了實(shí)現(xiàn)tryAcquireShared方法,獲取的是一個(gè)共享鎖,鎖在所有調(diào)用await方法的線程間共享,所以叫共享鎖。

如果獲取共享鎖失敗(返回了-1,說(shuō)明state不為0,也就是CountDownLatch的計(jì)數(shù)器還不為0),進(jìn)入調(diào)用doAcquireSharedInterruptibly方法中,按照我們上述的猜想,應(yīng)該是要將當(dāng)前線程放入到隊(duì)列中去。

在這之前,我們?cè)倩仡櫼幌翧QS隊(duì)列的數(shù)據(jù)結(jié)構(gòu):AQS是一個(gè)雙向鏈表,通過(guò)節(jié)點(diǎn)中的next,pre變量分別指向當(dāng)前節(jié)點(diǎn)后一個(gè)節(jié)點(diǎn)和前一個(gè)節(jié)點(diǎn)。其中,每個(gè)節(jié)點(diǎn)中都包含了一個(gè)線程和一個(gè)類(lèi)型變量:表示當(dāng)前節(jié)點(diǎn)是獨(dú)占節(jié)點(diǎn)還是共享節(jié)點(diǎn),頭節(jié)點(diǎn)中的線程為正在占有鎖的線程,而后的所有節(jié)點(diǎn)的線程表示為正在等待獲取鎖的線程。
進(jìn)入等待:

/**
     * Acquires in shared interruptible mode.
     * @param arg the acquire argument
     */
    private void doAcquireSharedInterruptibly(int arg)
        throws InterruptedException {
        // 插入等待隊(duì)列
        final Node node = addWaiter(Node.SHARED);
        boolean failed = true;
        try {
            for (;;) {
                final Node p = node.predecessor();
                // 如果是頭(可以理解為leader),則進(jìn)行一次waitStatus判斷
                if (p == head) {
                    int r = tryAcquireShared(arg);
                    if (r >= 0) {
                        // 設(shè)置頭 并喚醒之后可能等待的線程
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        failed = false;
                        return;
                    }
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

其中讓線程等待的代碼:

/**
     * Checks and updates status for a node that failed to acquire.
     * Returns true if thread should block. This is the main signal
     * control in all acquire loops.  Requires that pred == node.prev.
     *
     * @param pred node's predecessor holding status
     * @param node the node
     * @return {@code true} if thread should block
     */
    private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        // 是通過(guò)前一個(gè)節(jié)點(diǎn)的狀態(tài)進(jìn)行判斷的,如果前一個(gè)節(jié)點(diǎn)已經(jīng)是等待信號(hào)的狀態(tài),則當(dāng)前節(jié)點(diǎn)可以放心等待
        int ws = pred.waitStatus;
        if (ws == Node.SIGNAL)
            /*
             * This node has already set status asking a release
             * to signal it, so it can safely park.
             */
            return true;
        if (ws > 0) {
            /*
             * Predecessor was cancelled. Skip over predecessors and
             * indicate retry.
             */
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
            /*
             * waitStatus must be 0 or PROPAGATE.  Indicate that we
             * need a signal, but don't park yet.  Caller will need to
             * retry to make sure it cannot acquire before parking.
             */
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }

/**
     * Convenience method to park and then check if interrupted
     *
     * @return {@code true} if interrupted
     */
    private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this);
        return Thread.interrupted();
    }

這里有幾點(diǎn)需要說(shuō)明的:

  1. setHeadAndPropagate方法:
/**
     * Sets head of queue, and checks if successor may be waiting
     * in shared mode, if so propagating if either propagate > 0 or
     * PROPAGATE status was set.
     *
     * @param node the node
     * @param propagate the return value from a tryAcquireShared
     */
    private void setHeadAndPropagate(Node node, int propagate) {
        Node h = head; // Record old head for check below
        setHead(node);
        /*
         * Try to signal next queued node if:
         *   Propagation was indicated by caller,
         *     or was recorded (as h.waitStatus either before
         *     or after setHead) by a previous operation
         *     (note: this uses sign-check of waitStatus because
         *      PROPAGATE status may transition to SIGNAL.)
         * and
         *   The next node is waiting in shared mode,
         *     or we don't know, because it appears null
         *
         * The conservatism in both of these checks may cause
         * unnecessary wake-ups, but only when there are multiple
         * racing acquires/releases, so most need signals now or soon
         * anyway.
         */
        if (propagate > 0 || h == null || h.waitStatus < 0 ||
            (h = head) == null || h.waitStatus < 0) {
            Node s = node.next;
            if (s == null || s.isShared())
                doReleaseShared();
        }
    }

/**
     * Release action for shared mode -- signals successor and ensures
     * propagation. (Note: For exclusive mode, release just amounts
     * to calling unparkSuccessor of head if it needs signal.)
     */
    private void doReleaseShared() {
        /*
         * Ensure that a release propagates, even if there are other
         * in-progress acquires/releases.  This proceeds in the usual
         * way of trying to unparkSuccessor of head if it needs
         * signal. But if it does not, status is set to PROPAGATE to
         * ensure that upon release, propagation continues.
         * Additionally, we must loop in case a new node is added
         * while we are doing this. Also, unlike other uses of
         * unparkSuccessor, we need to know if CAS to reset status
         * fails, if so rechecking.
         */
        for (;;) {
            Node h = head;
            if (h != null && h != tail) {
                int ws = h.waitStatus;
                if (ws == Node.SIGNAL) { 
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0)) 
//如果當(dāng)前節(jié)點(diǎn)是SIGNAL意味著,它正在等待一個(gè)信號(hào),  
//或者說(shuō),它在等待被喚醒,因此做兩件事,1是重置waitStatus標(biāo)志位,2是重置成功后,喚醒下一個(gè)節(jié)點(diǎn)。
                        continue;            // loop to recheck cases
                    unparkSuccessor(h);
                }
                else if (ws == 0 &&
                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))  
//如果本身頭節(jié)點(diǎn)的waitStatus是出于重置狀態(tài)(waitStatus==0)的,將其設(shè)置為“傳播”狀態(tài)。
//意味著需要將狀態(tài)向后一個(gè)節(jié)點(diǎn)傳播。
                    continue;                // loop on failed CAS
            }
            if (h == head)                   // loop if head changed
                break;
        }
    }

為什么要這么做呢?這就是共享功能和獨(dú)占功能最不一樣的地方,對(duì)于獨(dú)占功能來(lái)說(shuō),有且只有一個(gè)線程(通常只對(duì)應(yīng)一個(gè)節(jié)點(diǎn),拿ReentantLock舉例,如果當(dāng)前持有鎖的線程重復(fù)調(diào)用lock()方法,那根據(jù)本系列上半部分我們的介紹,我們知道,會(huì)被包裝成多個(gè)節(jié)點(diǎn)在AQS的隊(duì)列中,所以用一個(gè)線程來(lái)描述更準(zhǔn)確),能夠獲取鎖,但是對(duì)于共享功能來(lái)說(shuō)。

共享的狀態(tài)是可以被共享的,也就是意味著其他AQS隊(duì)列中的其他節(jié)點(diǎn)也應(yīng)能第一時(shí)間知道狀態(tài)的變化。因此,一個(gè)節(jié)點(diǎn)獲取到共享狀態(tài)流程圖是這樣的:

比如現(xiàn)在有如下隊(duì)列:

當(dāng)Node1調(diào)用tryAcquireShared成功后,更換了頭節(jié)點(diǎn):


Node1變成了頭節(jié)點(diǎn)然后調(diào)用unparkSuccessor()方法喚醒了Node2、Node2中持有的線程A出于上面流程圖的park node的位置,

線程A被喚醒后,重復(fù)黃色線條的流程,重新檢查調(diào)用tryAcquireShared方法,看能否成功,如果成功,則又更改頭節(jié)點(diǎn),重復(fù)以上步驟,以實(shí)現(xiàn)節(jié)點(diǎn)自身獲取共享鎖成功后,喚醒下一個(gè)共享類(lèi)型節(jié)點(diǎn)的操作,實(shí)現(xiàn)共享狀態(tài)的向后傳遞。

2.其實(shí)對(duì)于doAcquireShared方法,AQS還提供了集中類(lèi)似的實(shí)現(xiàn):



分別對(duì)應(yīng)了:

  • 帶參數(shù)請(qǐng)求共享鎖。 (忽略中斷)
  • 帶參數(shù)請(qǐng)求共享鎖,且響應(yīng)中斷。(每次循環(huán)時(shí),會(huì)檢查當(dāng)前線程的中斷狀態(tài),以實(shí)現(xiàn)對(duì)線程中斷的響應(yīng))
  • 帶參數(shù)請(qǐng)求共享鎖但是限制等待時(shí)間。(第二個(gè)參數(shù)設(shè)置超時(shí)時(shí)間,超出時(shí)間后,方法返回。)
    比較特別的為最后一個(gè)doAcquireSharedNanos方法,我們一起看下它怎么實(shí)現(xiàn)超時(shí)時(shí)間的控制的。

因?yàn)樵摲椒ê推溆喃@取共享鎖的方法邏輯是類(lèi)似的,我用紅色框圈出了它所不一樣的地方,也就是實(shí)現(xiàn)超時(shí)時(shí)間控制的地方。

private boolean doAcquireSharedNanos(int arg, long nanosTimeout)
            throws InterruptedException {
        if (nanosTimeout <= 0L)
            return false;
        final long deadline = System.nanoTime() + nanosTimeout;
        final Node node = addWaiter(Node.SHARED);
        boolean failed = true;
        try {
            for (;;) {
                final Node p = node.predecessor();
                if (p == head) {
                    int r = tryAcquireShared(arg);
                    if (r >= 0) {
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        failed = false;
                        return true;
                    }
                }
                nanosTimeout = deadline - System.nanoTime();
                if (nanosTimeout <= 0L)
                    return false;
                if (shouldParkAfterFailedAcquire(p, node) &&
                    nanosTimeout > spinForTimeoutThreshold)
                    LockSupport.parkNanos(this, nanosTimeout);
                if (Thread.interrupted())
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

可以看到,其實(shí)就是在進(jìn)入方法時(shí),計(jì)算出了一個(gè)“deadline”,每次循環(huán)的時(shí)候用當(dāng)前時(shí)間和“deadline”比較,大于“dealine”說(shuō)明超時(shí)時(shí)間已到,直接返回方法。

注意這行代碼:

nanosTimeout > spinForTimeoutThreshold

從變量的字面意思可知,這是拿超時(shí)時(shí)間和超時(shí)自旋的最小作比較,在這里Doug Lea把超時(shí)自旋的閾值設(shè)置成了1000ns,即只有超時(shí)時(shí)間大于1000ns才會(huì)去掛起線程,否則,再次循環(huán),以實(shí)現(xiàn)“自旋”操作。這是“自旋”在AQS中的應(yīng)用之處。

看完await方法,我們?cè)賮?lái)看下countDown()方法:

public void countDown() {
        sync.releaseShared(1);
    }

public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {
            doReleaseShared();
            return true;
        }
        return false;
    }

// 其中CountDownLatch 的
protected boolean tryReleaseShared(int releases) {
            // Decrement count; signal when transition to zero
            for (;;) {
                int c = getState();
                // 如果早已是0 則已經(jīng)喚醒過(guò)
                if (c == 0)
                    return false;
                int nextc = c-1;
                
                if (compareAndSetState(c, nextc))
                    // 是通過(guò)更新計(jì)數(shù) 觸發(fā)到0 
                    return nextc == 0;
            }
        }

死循環(huán)更新state的值,實(shí)現(xiàn)state的減1操作,之所以用死循環(huán)是為了確保state值的更新成功。

從上文的分析中可知,如果state的值為0,在CountDownLatch中意味:所有的子線程已經(jīng)執(zhí)行完畢,這個(gè)時(shí)候可以喚醒調(diào)用await()方法的線程了,而這些線程正在AQS的隊(duì)列中,并被掛起的,

所以下一步應(yīng)該去喚醒AQS隊(duì)列中的頭節(jié)點(diǎn)了(AQS的隊(duì)列為FIFO隊(duì)列),然后由頭節(jié)點(diǎn)去依次喚醒AQS隊(duì)列中的其他共享節(jié)點(diǎn)。

如果tryReleaseShared返回true,進(jìn)入doReleaseShared()方法:

private void doReleaseShared() {
        /*
         * Ensure that a release propagates, even if there are other
         * in-progress acquires/releases.  This proceeds in the usual
         * way of trying to unparkSuccessor of head if it needs
         * signal. 
         * ===But if it does not, status is set to PROPAGATE to
         * ===ensure that upon release, propagation continues.
         * ===Additionally, we must loop in case a new node is added
         * ===while we are doing this. Also, unlike other uses of
         * ===unparkSuccessor, we need to know if CAS to reset status
         * ===fails, if so rechecking.
         */
        for (;;) {
            Node h = head;
            if (h != null && h != tail) {
                int ws = h.waitStatus;
                if (ws == Node.SIGNAL) {
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                        continue;            // loop to recheck cases
                    unparkSuccessor(h);
                }
                else if (ws == 0 &&
                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                    continue;                // loop on failed CAS
            }
            // 只有到了頭結(jié)點(diǎn),跳出循環(huán)
            if (h == head)                   // loop if head changed
                break;
        }
    }

/**
     * Wakes up node's successor, if one exists.
     *
     * @param node the node
     */
    private void unparkSuccessor(Node node) {
        /*
         * If status is negative (i.e., possibly needing signal) try
         * to clear in anticipation of signalling.  It is OK if this
         * fails or if status is changed by waiting thread.
         */
        int ws = node.waitStatus;
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);

        /*
         * Thread to unpark is held in successor, which is normally
         * just the next node.  But if cancelled or apparently null,
         * traverse backwards from tail to find the actual
         * non-cancelled successor.
         */
        Node s = node.next;
        if (s == null || s.waitStatus > 0) {
            s = null;
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)
                    s = t;
        }
        if (s != null)
            LockSupport.unpark(s.thread);
    }

這里有幾點(diǎn)值得注意:

  • 與AQS的獨(dú)占功能一樣,共享鎖是否可以被獲取的判斷為空方法,交由子類(lèi)去實(shí)現(xiàn)。
  • 與AQS的獨(dú)占功能不同,當(dāng)鎖被頭節(jié)點(diǎn)獲取后,獨(dú)占功能是只有頭節(jié)點(diǎn)獲取鎖,其余節(jié)點(diǎn)的線程繼續(xù)沉睡,等待鎖被釋放后,才會(huì)喚醒下一個(gè)節(jié)點(diǎn)的線程,而共享功能是只要頭節(jié)點(diǎn)獲取鎖成功,就在喚醒自身節(jié)點(diǎn)對(duì)應(yīng)的線程的同時(shí),繼續(xù)喚醒AQS隊(duì)列中的下一個(gè)節(jié)點(diǎn)的線程,每個(gè)節(jié)點(diǎn)在喚醒自身的同時(shí)還會(huì)喚醒下一個(gè)節(jié)點(diǎn)對(duì)應(yīng)的線程,以實(shí)現(xiàn)共享狀態(tài)的“向后傳播”,從而實(shí)現(xiàn)共享功能。

Ref:
不得不說(shuō)InfoQ上的文字質(zhì)量真不錯(cuò)!!
http://www.infoq.com/cn/articles/jdk1.8-abstractqueuedsynchronizer
http://www.infoq.com/cn/articles/java8-abstractqueuedsynchronizer
http://blog.csdn.net/pfnie/article/details/77599618
http://blog.csdn.net/chenssy/article/details/60479594

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,835評(píng)論 6 534
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,676評(píng)論 3 419
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 176,730評(píng)論 0 380
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 63,118評(píng)論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,873評(píng)論 6 410
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,266評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,330評(píng)論 3 443
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,482評(píng)論 0 289
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,036評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,846評(píng)論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,025評(píng)論 1 371
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,575評(píng)論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,279評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,684評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 35,953評(píng)論 1 289
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,751評(píng)論 3 394
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,016評(píng)論 2 375

推薦閱讀更多精彩內(nèi)容