隊(duì)列同步器AbstractQueuedSynchronizer源碼解析

AQS是用來構(gòu)建鎖或者其他同步組件的基礎(chǔ)框架调炬,能夠?qū)崿F(xiàn)大部分同步需求的基礎(chǔ)语盈,AQS基于volatile變量提供的鎖的內(nèi)存語義和CAS原子操作指令來實(shí)現(xiàn)多線程的同步機(jī)制。state同步狀態(tài)量為整型缰泡,并發(fā)locks包中還提供了AbstractQueuedSynchronizer的鏡像long類型state版本AbstractQueuedLongSynchronizer刀荒,支持更多的線程獲取同步狀態(tài)。

原理解析

等待隊(duì)列節(jié)點(diǎn)

static final class Node {
    /**共享節(jié)點(diǎn)模式*/
    static final Node SHARED = new Node();
    /** 獨(dú)占節(jié)點(diǎn)模式 */
    static final Node EXCLUSIVE = null;

    /** waitStatus 狀態(tài)的常量表示 */
    static final int CANCELLED =  1;
    static final int SIGNAL    = -1;
    static final int CONDITION = -2;
    static final int PROPAGATE = -3;
    volatile int waitStatus;

    // 前繼節(jié)點(diǎn)
    volatile Node prev;
    // 后繼節(jié)點(diǎn)
    volatile Node next;
    // 入隊(duì)的線程
    volatile Thread thread;

    // 等待在condition上的下一個(gè)節(jié)點(diǎn)(獨(dú)占模式)或者是特定的值SHARED用來標(biāo)示此節(jié)點(diǎn)是共享模式
    Node nextWaiter;

    // 如果當(dāng)前節(jié)點(diǎn)等待在共享模式上則返回true
    final boolean isShared() {
        return nextWaiter == SHARED;
    }

    // 返回前繼節(jié)點(diǎn)棘钞,如果前繼節(jié)點(diǎn)為null則拋出異常
    final Node predecessor() throws NullPointerException {
        Node p = prev;
        if (p == null)
            throw new NullPointerException();
        else
            return p;
    }
    // 默認(rèn)構(gòu)造函數(shù):用來初始化頭節(jié)點(diǎn)或者SHARED共享模式標(biāo)志
    Node() {
    }
    // 被addWaiter調(diào)用
    Node(Thread thread, Node mode) {
        this.nextWaiter = mode;
        this.thread = thread;
    }
    // 被Condition使用
    Node(Thread thread, int waitStatus) {
        this.waitStatus = waitStatus;
        this.thread = thread;
    }
}

等待狀態(tài)詳解:

  1. CANCELLED 取消狀態(tài)

由于在等待隊(duì)列中等待的線程等待超時(shí)或者被中斷缠借,需要從等待隊(duì)列中取消等待,之后這個(gè)狀態(tài)的節(jié)點(diǎn)狀態(tài)將不再發(fā)生變化武翎。處于這種狀態(tài)的節(jié)點(diǎn)會被踢出隊(duì)列烈炭,被GC回收

  1. SIGNAL 通知狀態(tài)

當(dāng)前節(jié)點(diǎn)的線程釋放了同步狀態(tài)或者被取消,將會喚醒后繼等待(阻塞)的節(jié)點(diǎn)宝恶,使得后繼節(jié)點(diǎn)的線程得以運(yùn)行符隙。

  1. CONDITION 條件等待狀態(tài)

當(dāng)前節(jié)點(diǎn)等待(阻塞)在Condition上,當(dāng)其他線程對Condition調(diào)用signal方法后垫毙,該節(jié)點(diǎn)將會從等待隊(duì)列中轉(zhuǎn)移到同步隊(duì)列中霹疫,加入對同步狀態(tài)的獲取中。

  1. PROPAGATE 傳播狀態(tài)

表示在下一次共享模式同步狀態(tài)獲取將會無條件地被傳播下去综芥。使用共享模式的head節(jié)點(diǎn)有可能處于這種狀態(tài)丽蝎。

  1. INITIAL 初始狀態(tài)

新建節(jié)點(diǎn)處于這種狀態(tài)

分析思路:分析主要方法的調(diào)用流程,洞察實(shí)現(xiàn)原理

獨(dú)占式同步狀態(tài)獲取與釋放

主要方法是acquire和release

acquire方法流程
  1. acquire 獨(dú)占式同步狀態(tài)的獲取膀藐,獲取過程中忽略中斷屠阻,但是最終會設(shè)置中斷標(biāo)志
    /**
     * 向外提供的獨(dú)占式獲取同步狀態(tài)的方法
     *
     * 
     * 第一次嘗試獲取同步狀態(tài),成功則返回额各,
     * 失敗則將當(dāng)前線程構(gòu)造成節(jié)點(diǎn)并添加到等待隊(duì)列
     * 并死循環(huán)獲取同步狀態(tài)
     * 如果線程被中斷則設(shè)置當(dāng)前線程的中斷狀態(tài)
     */
    public final void acquire(long arg) {
        // 第一次嘗試獲取同步狀態(tài)国觉,成功則返回
        if (!tryAcquire(arg) && 
            // 失敗則構(gòu)造節(jié)點(diǎn),入隊(duì)虾啦,死循環(huán)獲取
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            // 設(shè)置中斷標(biāo)志
            selfInterrupt();
    }
  1. tryAcquire
    /**
    * 需要重寫的自定義方法麻诀,該方法需要保證線程
    * 安全的獲取同步狀態(tài)(CAS痕寓,用AQS提供的方法實(shí)現(xiàn))
    * @return 獲取成功則返回true
    */
   protected boolean tryAcquire(long arg) {
       throw new UnsupportedOperationException();
   }
  1. addWaiter
    /**
     * 為當(dāng)前線程以及給定模式創(chuàng)建新節(jié)點(diǎn)并入隊(duì)
     *
     * @param mode 節(jié)點(diǎn)等待模式
     * @return node 新添加的節(jié)點(diǎn)
     */
    private Node addWaiter(Node mode) {
        Node node = new Node(Thread.currentThread(), mode);
        // 進(jìn)行一次入隊(duì)嘗試,失敗則有enq的死循環(huán)CAS來保證最后能入隊(duì)
        Node pred = tail;
        if (pred != null) {
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        // 循環(huán)CAS來保證最后能入隊(duì)
        enq(node);
        return node; // 返回新添加的等待節(jié)點(diǎn)
    }
  1. enq
    /**
     * 線程安全地插入節(jié)點(diǎn)到等待隊(duì)列中(死循環(huán)CAS)
     * 當(dāng)尾節(jié)點(diǎn)為null時(shí)插入前需要初始化頭結(jié)點(diǎn)
     * @param node 入隊(duì)的節(jié)點(diǎn)
     * @return t 入隊(duì)節(jié)點(diǎn)的前繼節(jié)點(diǎn)
     */
    private Node enq(final Node node) {
        // 循環(huán)CAS將節(jié)點(diǎn)入隊(duì)
        for (;;) { 
            Node t = tail;
            // 當(dāng)尾節(jié)點(diǎn)為null時(shí)表明:則說明等待隊(duì)列為空
            // 需要初始化一個(gè)頭尾相等的等待隊(duì)列蝇闭。
            // head和tail的延遲初始化呻率。
            if (t == null) { // Must initialize
                if (compareAndSetHead(new Node()))// 初始化需要 CAS 設(shè)置頭節(jié)點(diǎn)
                    tail = head; // 頭尾相等
            }
            // 尾節(jié)點(diǎn)不為null,說明等待隊(duì)列不為空呻引,可以插入 
            else {
                node.prev = t; // 插入節(jié)點(diǎn)和尾節(jié)點(diǎn)前向關(guān)聯(lián)礼仗,防止斷裂
                if (compareAndSetTail(t, node)) { // CAS 插入尾節(jié)點(diǎn)
                    t.next = node; // 后向關(guān)聯(lián)
                    return t; // 返回插入節(jié)點(diǎn)的前繼節(jié)點(diǎn)
                }
            }
        }
    }
  1. acquireQueued
 /**
  * 每一個(gè)加入等待隊(duì)列的節(jié)點(diǎn)線程通過自旋獲取同步狀態(tài)
  * 判斷自己的前繼節(jié)點(diǎn)是否為頭結(jié)點(diǎn),如果是則嘗試獲取同步狀態(tài)
  * 成功則返回逻悠,否則根據(jù)前繼節(jié)點(diǎn)是否為SIGNAL來阻塞線程
  *
  * @param node 入隊(duì)的節(jié)點(diǎn)
  * @return 等待過程中發(fā)生中斷則返回true
  */
 final boolean acquireQueued(final Node node, long arg) {
     // 獲取失敗標(biāo)志初始化為true(第一次確實(shí)失敗了)
     boolean failed = true;
     try {
         // 線程中斷標(biāo)志
         boolean interrupted = false;
         // 自旋獲取同步狀態(tài)
         for (;;) {
             // 如果為null拋出異常
             final Node p = node.predecessor();

             // 判斷前繼節(jié)點(diǎn)是否為head節(jié)點(diǎn)藐守,
             // 如果是則嘗試獲取同步狀態(tài)
             if (p == head && tryAcquire(arg)) {

                 // 成功獲取同步狀態(tài),則將當(dāng)前節(jié)點(diǎn)設(shè)置為head結(jié)點(diǎn)
                 // 在獨(dú)占模式下設(shè)置head節(jié)點(diǎn)蹂风,不需要考慮線程問題,
                 // 因?yàn)橥粫r(shí)刻只有一個(gè)線程能獲取同步狀態(tài)
                 setHead(node); 

                 // head節(jié)點(diǎn)的prev和thread引用為null乾蓬,如果next引用也為null則將被GC
                 // 斷開head節(jié)點(diǎn)的所有引用惠啄,幫助GC
                 p.next = null; 

                 failed = false;
                 return interrupted;
             }
             // 如果當(dāng)前節(jié)點(diǎn)前繼不是head節(jié)點(diǎn),或者是前繼節(jié)點(diǎn)head節(jié)點(diǎn)但是
             // 沒有獲取到同步狀態(tài)

             // 而且如果需要阻塞線程任内,則阻塞線程撵渡,并將線程中斷標(biāo)志
             // 重置,并設(shè)置interrupted為重置前的中斷狀態(tài)
             if (shouldParkAfterFailedAcquire(p, node) &&
                 parkAndCheckInterrupt())
                 interrupted = true;
         }
     }
     // 前面死循環(huán)自旋死嗦,只有成功獲取才能退出循環(huán)趋距,而且還會設(shè)置failed為false
     // 所以我在想什么時(shí)候failed為true,并執(zhí)行cancelAcquire操作
     // 后來發(fā)現(xiàn)在 node.predecessor() 方法中當(dāng)前繼節(jié)點(diǎn)為null時(shí)會拋出
     // NullPonitException異常越除,看來當(dāng)前節(jié)點(diǎn)沒有前繼節(jié)點(diǎn)時(shí)需要取消獲取的操作
     // 這是由于取消狀態(tài)的節(jié)點(diǎn)從等待隊(duì)列中移除节腐,沒有關(guān)聯(lián)的前節(jié)點(diǎn)而導(dǎo)致,
     // 因?yàn)閔ead節(jié)點(diǎn)一定是獲取到同步狀態(tài)的線程摘盆,在成功獲取同步狀態(tài)線程會
     // 立刻返回翼雀。
     finally {
         if (failed) 
             cancelAcquire(node);
     }
 }

值得注意的是,在獲取同步狀態(tài)的主循環(huán)中孩擂,僅僅是記錄中斷狀態(tài)狼渊,然后將中斷狀態(tài)返回,在acquire中則根據(jù)這個(gè)中斷狀態(tài)类垦,在獲取成功返回前設(shè)置這個(gè)中斷標(biāo)志狈邑,經(jīng)歷如下過程:
**parkAndCheckInterrupt重置返回→acquireQueued記錄并返回→acquire重新設(shè)置 **

成功獲取同步狀態(tài)的線程在這個(gè)方法里** 只是調(diào)用setHead設(shè)置head節(jié)點(diǎn),但是并沒有修改head的等待狀態(tài)蚤认。米苹,當(dāng)釋放同步狀態(tài)時(shí)會將head節(jié)點(diǎn)狀態(tài)CAS設(shè)置為初始狀態(tài),然后喚醒后繼阻塞的線程烙懦,被喚醒的線程會檢查前繼節(jié)點(diǎn)以及其狀態(tài)驱入,當(dāng)獲取失敵喑础(隱含有線程成功獲取同步狀態(tài),并設(shè)置了新的head)**會確保前繼節(jié)點(diǎn)的狀態(tài)為SIGNAL

什么時(shí)候會執(zhí)行finally語句塊的內(nèi)容亏较?

比如fullyRelease方式失敗莺褒,會將節(jié)點(diǎn)狀態(tài)設(shè)置為CANCEL,詳解見上面代碼注釋

  1. setHead
    /**
     * 將給定的節(jié)點(diǎn)設(shè)置為頭結(jié)點(diǎn)
     * 等待隊(duì)列初始化后雪情,只有獲得同步狀態(tài)的線程才能設(shè)置頭結(jié)點(diǎn)
     * 所以不需要循環(huán)CAS來設(shè)置頭結(jié)點(diǎn)
     * head節(jié)點(diǎn)只是獲得同步狀態(tài)的線程的通知阻塞線程的載體
     * @param node 新的head節(jié)點(diǎn)
     */
    private void setHead(Node node) {
        head = node;
        node.thread = null; // 并不保存獲得同步狀態(tài)線程信息
        node.prev = null; // 前繼節(jié)點(diǎn)為null
    }

獨(dú)占模式下獲取到同步狀態(tài)的線程的節(jié)點(diǎn)的狀態(tài)不會在setHead之后發(fā)生變化遵岩,也不會影響后繼節(jié)點(diǎn)。但是在共享模式下會將發(fā)生變化巡通,見共享模式

  1. shouldParkAfterFailedAcquire
   /**
     * 檢查并設(shè)置獲取失敗的節(jié)點(diǎn)的等待狀態(tài)
     * 新加入的節(jié)點(diǎn)要么獲取同步狀態(tài)要么阻塞尘执,
     * 如果當(dāng)前節(jié)點(diǎn)是head結(jié)點(diǎn),則tryAcquire獲取同步狀態(tài)
     * 如果當(dāng)前節(jié)點(diǎn)不是head結(jié)點(diǎn)宴凉,則需要判斷是否需要阻塞
     * 這個(gè)方法是整個(gè)acquire循環(huán)中主要的信號控制
     * @param pred node的前繼節(jié)點(diǎn)
     * @param node 
     */
    private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        // 前繼節(jié)點(diǎn)的等待狀態(tài)
        int ws = pred.waitStatus;

        // 如果前繼節(jié)點(diǎn)為通知狀態(tài)
        // 表示后繼阻塞的節(jié)點(diǎn)能夠被前繼節(jié)點(diǎn)喚醒(釋放或取消)誊锭,所以大膽的阻塞當(dāng)前節(jié)點(diǎn)
        // 則返回true,表示應(yīng)該阻塞當(dāng)前節(jié)點(diǎn)
        if (ws == Node.SIGNAL)
            return true;

        // 如果為取消狀態(tài)弥锄,則將當(dāng)前節(jié)點(diǎn)的前繼節(jié)點(diǎn)設(shè)置為
        // 向前遍歷不為取消狀態(tài)的第一個(gè)節(jié)點(diǎn)丧靡,并關(guān)聯(lián)彼此。
        // 其實(shí)相當(dāng)于在等待隊(duì)列中刪除取消狀態(tài)的節(jié)點(diǎn)籽暇。
        if (ws > 0) {
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        }

        // 如果前繼節(jié)點(diǎn)不為取消狀態(tài)温治,則表明需要一個(gè)通知的信號
        // 設(shè)置前繼節(jié)點(diǎn)為通知狀態(tài),但是不會阻塞線程戒悠。
        // 并由調(diào)用者在下一次嘗試保證
        // 在阻塞之前不能進(jìn)行獲取同步狀態(tài)的操作
        else {
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }

對當(dāng)前節(jié)點(diǎn)的前繼節(jié)點(diǎn)進(jìn)行判斷:

  1. 當(dāng)為狀態(tài)為SIGNAL時(shí)則返回true熬荆,
  2. 當(dāng)為CANCEL時(shí)則刪除當(dāng)前節(jié)點(diǎn)的狀態(tài)為CANCEL的前繼節(jié)點(diǎn),直到第一個(gè)非CANCEL節(jié)點(diǎn)并關(guān)聯(lián)彼此绸狐。
  3. 狀態(tài)為INITIAL卤恳、PROPAGATE和CONDITION時(shí),則將前繼節(jié)點(diǎn)設(shè)置為SIGNAL六孵,確保前繼節(jié)點(diǎn)能夠通知到當(dāng)前節(jié)點(diǎn)纬黎,并返回false,不阻塞劫窒,進(jìn)行下一次循環(huán)獲取本今。

前繼節(jié)點(diǎn)為SIGNAL狀態(tài)的線程都需要阻塞,當(dāng)前繼節(jié)點(diǎn)不為SIGNAL主巍,要設(shè)置為SIGNAL冠息,即始終保持前繼節(jié)點(diǎn)的SIGNAL狀態(tài)。

  1. parkAndCheckInterrupt
    /**
     * 阻塞當(dāng)前線程
     * @return 如果當(dāng)前線程被中斷則返回true
     */
    private final boolean parkAndCheckInterrupt() {
        // 參數(shù)this為阻塞對象孕索,用來標(biāo)識當(dāng)前線程在等待的對象逛艰,
        // 該對象用來問題排查和系統(tǒng)監(jiān)控
        LockSupport.park(this);

        // 清除中斷標(biāo)志,并返回清除之前的中斷狀態(tài)
        return Thread.interrupted();
    }
  1. selfInterrupt
    /**
     * 設(shè)置當(dāng)前線程的中斷標(biāo)志
     */
    static void selfInterrupt() {
        Thread.currentThread().interrupt();
    }
  1. cancelAcquire
    /**
     * 取消CANCEL狀態(tài)的節(jié)點(diǎn)嘗試的獲取操作
     *
     * @param node CANCEL狀態(tài)的節(jié)點(diǎn)
     */
    private void cancelAcquire(Node node) {
        // 節(jié)點(diǎn)為null則返回
        if (node == null)
            return;

        // 清除線程引用搞旭,幫助GC
        node.thread = null;

        // 跳過取消的前繼節(jié)點(diǎn)
        Node pred = node.prev;
        while (pred.waitStatus > 0)
            node.prev = pred = pred.prev;

        // 保存前繼節(jié)點(diǎn)的后繼節(jié)點(diǎn)引用
        Node predNext = pred.next;

        // 設(shè)置當(dāng)前節(jié)點(diǎn)的狀態(tài)為取消狀態(tài)
        node.waitStatus = Node.CANCELLED;

        // 如果當(dāng)前節(jié)點(diǎn)為尾節(jié)點(diǎn)散怖,則將前繼節(jié)點(diǎn)設(shè)置為尾節(jié)點(diǎn)
        // 并將尾節(jié)點(diǎn)的后繼節(jié)點(diǎn)設(shè)置為null
        if (node == tail && compareAndSetTail(node, pred)) {
            compareAndSetNext(pred, predNext, null);
        } 
        // 如果當(dāng)前節(jié)點(diǎn)不為尾節(jié)點(diǎn)菇绵,或者為尾節(jié)點(diǎn),但是設(shè)置尾節(jié)點(diǎn)時(shí)失敗镇眷,
        // 此時(shí)當(dāng)前節(jié)點(diǎn)可能存在后繼節(jié)點(diǎn)咬最,替代它成為尾節(jié)點(diǎn)
        else {
            // 如果后繼節(jié)點(diǎn)能夠被通知,則將當(dāng)前節(jié)點(diǎn)的前繼節(jié)點(diǎn)的后繼節(jié)點(diǎn)設(shè)置
            // 為當(dāng)前節(jié)點(diǎn)后繼節(jié)點(diǎn)欠动,否則直接喚醒當(dāng)前節(jié)點(diǎn)的后繼節(jié)點(diǎn)永乌。
            int ws;
            if (pred != head &&
                ((ws = pred.waitStatus) == Node.SIGNAL ||
                 (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
                pred.thread != null) {
                Node next = node.next;
                if (next != null && next.waitStatus <= 0)
                    compareAndSetNext(pred, predNext, next);
            } else {
                unparkSuccessor(node);
            }

            node.next = node; // help GC
        }
    }

將取消獲取的節(jié)點(diǎn)的狀態(tài)設(shè)置為CANCEL,并剔除具伍,然后確保調(diào)整后的節(jié)點(diǎn)的前繼節(jié)點(diǎn)為SIGNAL狀態(tài)翅雏。

release方法流程
  1. release
    /**
     * 釋放同步狀態(tài)
     * @return tryRelease返回true則返回true
     */
    public final boolean release(long arg) {
        // 修改同步狀態(tài)成功
        if (tryRelease(arg)) { 
            Node h = head;
            // 如果head節(jié)點(diǎn)不為null,而且狀態(tài)不為初始狀態(tài)
            // 則喚醒head后繼節(jié)點(diǎn)
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

獨(dú)占模式下人芽,只有獲取到同步狀態(tài)的線程才能釋放望几,所以不存在線程安全的問題

  1. tryRelease
    /**
     * 獨(dú)占模式釋放同步狀態(tài),不需要保證線程并發(fā)
     * 需要實(shí)現(xiàn)此方法萤厅,為釋放同步狀態(tài)修改state值
     */
    protected boolean tryRelease(long arg) {
        throw new UnsupportedOperationException();
    }
  1. unparkSuccessor
    /**
     * 喚醒node節(jié)點(diǎn)存在的一個(gè)后繼節(jié)點(diǎn)
     * 注意:喚醒的節(jié)點(diǎn)并不一定能獲取到同步狀態(tài)橄妆,
     * 所以setHead方法并在這里調(diào)用,而是在acquireQueued
     * 中成功獲取同步狀態(tài)后調(diào)用
     */
    private void unparkSuccessor(Node node) {
        /*
         * 當(dāng)節(jié)點(diǎn)的等待狀態(tài)waitStatus為負(fù)值時(shí)祈坠,比如可能通知后繼阻塞節(jié)點(diǎn)執(zhí)行等,
         * 則需要重置狀態(tài)值為初始值矢劲,這里沒有采用循環(huán)CAS設(shè)置赦拘,即使這一次嘗試由于
         * CAS失敗或者等待線程修改狀態(tài)而導(dǎo)致失敗都是允許的。
         */
        int ws = node.waitStatus;
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);

        /*
         * 如果后繼節(jié)點(diǎn)為null或者為CANCELLED取消狀態(tài)則從尾節(jié)點(diǎn)開始向前遍歷直到
         * 找到一個(gè)為非CANCELLED狀態(tài)的節(jié)點(diǎn)芬沉。
         */
        Node s = node.next;
        if (s == null || s.waitStatus > 0) {
            s = null;
            // 從尾節(jié)點(diǎn)向前遍歷到node節(jié)點(diǎn)之前找到非CANCELLED節(jié)點(diǎn)
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)
                    s = t;
        }
        // 如果遍歷后找到非CANCELLED狀態(tài)的節(jié)點(diǎn)則喚醒節(jié)點(diǎn)的線程
        if (s != null)
            LockSupport.unpark(s.thread);// 喚醒非CANCELLED狀態(tài)的線程
    }

喚醒head后繼的有效節(jié)點(diǎn)图筹,如果head后繼節(jié)點(diǎn)為null或者為取消狀態(tài)钧唐,則從后向前遍歷找到第一個(gè)有效的節(jié)點(diǎn),被喚醒的節(jié)點(diǎn)會刪除前繼為取消狀態(tài)的節(jié)點(diǎn)(shouldParkAfterFailedAcquire()方法中),喚醒的節(jié)點(diǎn)加入對同步狀態(tài)的獲取中
注意:被喚醒的線程并不一定能獲得同步狀態(tài)

獨(dú)占式超時(shí)同步狀態(tài)獲取

隊(duì)列同步器AbstractQueuedSynchronizer源碼解析-續(xù)1

共享式同步狀態(tài)獲取與釋放

隊(duì)列同步器AbstractQueuedSynchronizer源碼解析-續(xù)2

同步隊(duì)列的使用方式

隊(duì)列同步器AbstractQueuedSynchronizer源碼解析-AQS使用

總結(jié)
  1. 從喚醒的線程競爭獲取同步狀態(tài)來看暖庄,AQS的獲取并非絕對公平的,因?yàn)槊恳粋€(gè)新增的節(jié)點(diǎn)獲取立馬競爭同步狀態(tài)灵寺,如果此時(shí)恰巧獲取成功則這種獲取是非公平的诫尽,失敗才會添加到等待隊(duì)列當(dāng)中,等待隊(duì)列內(nèi)部競爭才是FIFO的憔维,是公平的涛救。

  2. 如何做到公平獲取:需要tryAcquire和tryAcquireShared自定義方法中需要調(diào)用hasQueuedPredecessors方法來判斷等待隊(duì)列中是否含有不為當(dāng)前線程的head的后繼節(jié)點(diǎn)业扒,如果有检吆,則說明當(dāng)前線程并不能立馬去獲取同步狀態(tài),而是加入等待隊(duì)列中以FIFO來公平獲取同步狀態(tài)程储。

hasQueuedPredecessors方法
hasQueuedPredecessors方法

重入公平鎖tryAcquire方法實(shí)例
image.png
  1. AQS中等待隊(duì)列的每個(gè)節(jié)點(diǎn)可以是獨(dú)占模式的也可以是共享模式的(即獨(dú)占模式和共享模式都共享自同一FIFO隊(duì)列)蹭沛,但是一般情況下臂寝,等待隊(duì)列中的所有節(jié)點(diǎn)的等待模式一般為同一種模式,比如ReentrantLock重入鎖摊灭,僅需重寫對應(yīng)的獲取和釋放的方法咆贬。也可以所有節(jié)點(diǎn)不為同一等待模式,如ReadWriteLock讀寫鎖斟或,需要重寫不同模式的獲取和釋放方法素征。

  2. ConditionObject被定義為AQS內(nèi)部公共類,用來向外暴露給調(diào)用者萝挤;ConditionObject實(shí)現(xiàn)了Condition接口御毅,用于在獨(dú)占模式下。使用ConditionObject必須重寫isHeldExclusively方法怜珍,因?yàn)镃onditionObject多處調(diào)用了這個(gè)方法端蛆。ConditionObject詳解

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市酥泛,隨后出現(xiàn)的幾起案子今豆,更是在濱河造成了極大的恐慌,老刑警劉巖柔袁,帶你破解...
    沈念sama閱讀 217,734評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件呆躲,死亡現(xiàn)場離奇詭異,居然都是意外死亡捶索,警方通過查閱死者的電腦和手機(jī)插掂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來腥例,“玉大人辅甥,你說我怎么就攤上這事×鞘” “怎么了璃弄?”我有些...
    開封第一講書人閱讀 164,133評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長构回。 經(jīng)常有香客問我夏块,道長,這世上最難降的妖魔是什么纤掸? 我笑而不...
    開封第一講書人閱讀 58,532評論 1 293
  • 正文 為了忘掉前任拨扶,我火速辦了婚禮,結(jié)果婚禮上茁肠,老公的妹妹穿的比我還像新娘患民。我一直安慰自己,他們只是感情好垦梆,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,585評論 6 392
  • 文/花漫 我一把揭開白布匹颤。 她就那樣靜靜地躺著仅孩,像睡著了一般。 火紅的嫁衣襯著肌膚如雪印蓖。 梳的紋絲不亂的頭發(fā)上辽慕,一...
    開封第一講書人閱讀 51,462評論 1 302
  • 那天,我揣著相機(jī)與錄音赦肃,去河邊找鬼溅蛉。 笑死,一個(gè)胖子當(dāng)著我的面吹牛他宛,可吹牛的內(nèi)容都是我干的船侧。 我是一名探鬼主播,決...
    沈念sama閱讀 40,262評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼厅各,長吁一口氣:“原來是場噩夢啊……” “哼镜撩!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起队塘,我...
    開封第一講書人閱讀 39,153評論 0 276
  • 序言:老撾萬榮一對情侶失蹤袁梗,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后憔古,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體遮怜,經(jīng)...
    沈念sama閱讀 45,587評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,792評論 3 336
  • 正文 我和宋清朗相戀三年鸿市,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了奈泪。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,919評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡灸芳,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出拜姿,到底是詐尸還是另有隱情烙样,我是刑警寧澤,帶...
    沈念sama閱讀 35,635評論 5 345
  • 正文 年R本政府宣布蕊肥,位于F島的核電站谒获,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏壁却。R本人自食惡果不足惜批狱,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,237評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望展东。 院中可真熱鬧赔硫,春花似錦、人聲如沸盐肃。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至推盛,卻和暖如春峦阁,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背耘成。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評論 1 269
  • 我被黑心中介騙來泰國打工榔昔, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人瘪菌。 一個(gè)月前我還...
    沈念sama閱讀 48,048評論 3 370
  • 正文 我出身青樓撒会,卻偏偏與公主長得像,于是被迫代替她去往敵國和親控嗜。 傳聞我的和親對象是個(gè)殘疾皇子茧彤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,864評論 2 354

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