ReentrantLock理解AQS同步隊列的細節(jié)和設(shè)計模式

Lock接口

public interface Lock {
  //加鎖
  void lock();
  //可中斷鎖與Lock不同之處在于可響應(yīng)中斷操作邀泉,即在獲取鎖的過程中可以中斷
  void lockInterruptibly() throws InterruptedException;
  //立即返回的獲取鎖,返回true則表示成功顽馋,false表示失敗
  boolean tryLock();
//根據(jù)傳入時間立即返回的獲取鎖,返回true表示成功,false表示失敗
  boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
  //釋放鎖
  void unlock();

  Condition newCondition();
}

Lock的實現(xiàn)

實現(xiàn)Lock接口的類有很多炕倘,以下為幾個常見的鎖實現(xiàn)

  • ReentrantLock:表示重入鎖,它是唯一一個實現(xiàn)了Lock接口的類翰撑。重入鎖指的是線程在獲得鎖之后罩旋,再次獲取該鎖不需要阻塞,而是直接關(guān)聯(lián)一次計數(shù)器增加重入次數(shù)
  • ReentrantReadWriteLock:重入讀寫鎖眶诈,它實現(xiàn)了ReadWriteLock接口涨醋,在這個類中維護了兩個鎖,一個是ReadLock逝撬,一個是WriteLock浴骂,他們都分別實現(xiàn)了Lock接口。讀寫鎖是一種適合讀多寫少的場景下解決線程安全問題的工具宪潮,基本原則是:讀和讀不互斥溯警、讀和寫互斥、寫和寫互斥狡相。 也就是說涉及到影響數(shù)據(jù)變化的操作都會存在互斥梯轻。
  • StampedLock: stampedLock是JDK8引入的新的鎖機制,可以簡單認為是讀寫鎖的一個改進版本谣光,讀寫鎖雖然通過分離讀和寫的功能使得讀和讀之間可以完全并發(fā)檩淋,但是讀和寫是有沖突的,如果大量的讀線程存在萄金,可能會引起寫線程的饑餓蟀悦。stampedLock是一種樂觀的讀策略,使得讀線程完全不會阻塞寫線程氧敢。和ReadWriteLock相比日戈,寫入的加鎖是完全一樣的,不同的是讀取孙乖。注意到首先我們通過tryOptimisticRead()獲取一個樂觀讀鎖浙炼,并返回版本號。接著進行讀取唯袄,讀取完成后弯屈,我們通過validate()去驗證版本號,如果在讀取過程中沒有寫入恋拷,版本號不變资厉,驗證成功,我們就可以放心地繼續(xù)后續(xù)操作蔬顾。如果在讀取過程中有寫入宴偿,版本號會發(fā)生變化湘捎,驗證將失敗。在失敗的時候窄刘,我們再通過獲取悲觀讀鎖再次讀取窥妇。由于寫入的概率不高,程序在絕大部分情況下可以通過樂觀讀鎖獲取數(shù)據(jù)娩践,極少數(shù)情況下使用悲觀讀鎖獲取數(shù)據(jù)
    另外是不可重入鎖活翩,不能在一個線程中反復(fù)獲取同一個鎖。
我們一般使用ReentrantLock:
Lock lock=new ReentrantLock()
lock.lock();
try{
//代碼....
}finally{
lock.unlock();
}

ReentrantLock默認是非公平鎖欺矫,我們先來了解下

公平鎖和非公平鎖特點

公平鎖是指多個線程按照申請鎖的順序來獲取鎖纱新,線程直接進入FIFO隊列,隊列中的第一個線程才能獲得鎖穆趴。公平鎖的優(yōu)點是等待鎖的線程不會夯死脸爱。缺點是吞吐效率相對非公平鎖要低,等待隊列中除了第一個線程以外的所有線程都會阻塞未妹,CPU喚醒阻塞線程的開銷比非公平鎖大簿废。
非公平鎖是多個線程加鎖時直接嘗試獲取鎖,獲取不到才會到等待隊列的隊尾等待络它。但是如果此時鎖剛好可用族檬,那么該線程可以無需阻塞直接獲取到鎖,所以非公平鎖有可能出現(xiàn)后申請鎖的線程先獲取到鎖的場景化戳。 非公平鎖的優(yōu)點就是可以減少喚起線程的開銷(因為可能有的線程可以直接獲取到鎖单料,CPU也就不用喚醒它),所以整體的吞吐效率高。缺點是處于等待隊列中的線程可能會夯死(如果每次有線程來点楼,它恰巧每次都獲取到鎖扫尖,此時還在排隊等待鎖的線程就悲劇了),或者等很久才會獲得鎖掠廓。

ReentrantLock與AQS
public class ReentrantLock implements Lock, java.io.Serializable {
 private final Sync sync;

 /**
     * Base of synchronization control for this lock. Subclassed
     * into fair and nonfair versions below. Uses AQS state to
     * represent the number of holds on the lock.
     */
abstract static class Sync extends AbstractQueuedSynchronizer {
........
}

 /**
     * Sync object for non-fair locks
     */
static final class NonfairSync extends Sync {
      .....
}

 /**
     * Sync object for fair locks
     */
    static final class FairSync extends Sync {
      ....
    }


}
  • ReentrantLock實現(xiàn)了Lock接口换怖,內(nèi)部類有Sync,NonfairSync,FairSync(他們?nèi)齻€繼承了AQS),創(chuàng)建ReentrantLock時可以指定是非公平鎖還是公平鎖
  • Sync:是抽象類蟀瞧,實現(xiàn)了tryRelease方法沉颂,tryAccquire方法由它的子類NonfairSync,FairSync自己實現(xiàn)。
  • AQS:是一個抽象類悦污,但是代碼中卻沒有一個抽象方法铸屉,其中獲取鎖(tryAcquire方法)和釋放鎖(tryRelease方法)并沒有提供默認實現(xiàn),需要子類重寫這個兩個方法實現(xiàn)具體邏輯
  • Node:AQS的內(nèi)部類切端,本質(zhì)上是一個雙向鏈表抬探,用來管理獲取鎖的線程。
Node和AQS工作原理

AQS是提供基礎(chǔ)設(shè)施帆赢,如構(gòu)建同步隊列小压,控制同步狀態(tài)等,那么它是如何構(gòu)建同步隊列的呢椰于?它的工作原理是怎么樣的呢怠益?

public abstract class AbstractQueuedSynchronizer
    extends AbstractOwnableSynchronizer
    implements java.io.Serializable {
  //指向同步隊列對頭
 private transient volatile Node head;
//指向同步隊列的隊尾
private transient volatile Node tail;

//同步狀態(tài),0代表鎖未被占用,1代表鎖已經(jīng)占用
 /**
     * The synchronization state.
     */
    private volatile int state;
  .......
}

再看看Node這個內(nèi)部類:它是對每一個訪問同步代碼塊的線程的封裝瘾婿,關(guān)于等待狀態(tài)蜻牢,我們暫時只需要關(guān)注SINAL和初始化狀態(tài)即可

static final class Node {
      static final Node SHARED = new Node();
      static final Node EXCLUSIVE = null;
      static final int CANCELLED =  1;
      static final int SIGNAL    = -1;
      static final int CONDITION = -2;
      static final int PROPAGATE = -3;
      volatile int waitStatus;
      volatile Node prev; //前驅(qū)節(jié)點
      volatile Node next; //后繼節(jié)點
      volatile Thread thread;//當(dāng)前線程
      Node nextWaiter; //存儲在condition隊列中的后繼節(jié)點
      //是否為共享鎖
      final boolean isShared() { 
          return nextWaiter == SHARED;
      }

      final Node predecessor() throws NullPointerException {
          Node p = prev;
          if (p == null)
              throw new NullPointerException();
          else
              return p;
      }

      Node() {    // Used to establish initial head or SHARED marker
      }
      //將線程構(gòu)造成一個Node,添加到等待隊列
    Node(Node nextWaiter) { // Used by addWaiter
          this.nextWaiter = nextWaiter;
          U.putObject(this, THREAD, Thread.currentThread());
      }
      //這個方法會在Condition隊列使用偏陪,
      Node(int waitStatus) { // Used by Condition
         U.putInt(this, WAITSTATUS, waitStatus);
          U.putObject(this, THREAD, Thread.currentThread());
      }
  }
  • AQS本質(zhì)上就是由Node構(gòu)成的雙向鏈表抢呆,內(nèi)部有node head和node tail。
  • AQS內(nèi)部通過state來控制同步狀態(tài)笛谦,當(dāng)state=0時抱虐,則說明沒有任何線程占有共享資源,當(dāng)state=1時饥脑,則說明有線程目前正在使用共享變量恳邀,其他線程必須加入同步隊列進行等待;
  • AQS內(nèi)部通過內(nèi)部類Node構(gòu)成FIFO的同步隊列來完成線程獲取鎖的排隊工作灶轰,同時利用內(nèi)部類ConditionObject構(gòu)建等待隊列谣沸,當(dāng)Condtion調(diào)用wait()方法后,線程將會加入等待隊列中笋颤,而當(dāng)Condtion調(diào)用signal()方法后乳附,線程將從等待隊列轉(zhuǎn)移同步隊列中進行鎖競爭。注意這里涉及到兩種隊列伴澄,一種同步隊列赋除,當(dāng)線程請求鎖而等待,后將加入同步隊列秉版,另一種則是等待隊列贤重,通過Condition調(diào)用await()方法釋放鎖后,加入等待隊列清焕。
  • 當(dāng)我們調(diào)用ReentrantLock.lock()方法時并蝗,實際操作的是基于Node結(jié)構(gòu)的同步隊列,此時AQS中的state變量則是代表同步狀態(tài)秸妥,加鎖后滚停,如果此時state的值為0,則說明當(dāng)前線程可以獲取到鎖粥惧,同時將state設(shè)置為1键畴,則表示獲取成功。如果調(diào)用ReentrantLock.lock()方法時sate已為1,也就是當(dāng)前鎖已被其他線程持有起惕,那么當(dāng)前執(zhí)行線程將被封裝為Node節(jié)點加入同步隊列等待.


    image.png

獲取鎖

 public ReentrantLock() {
        sync = new NonfairSync();
    }

public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

//加鎖操作
 public void lock() {
        sync.lock();
    }

這里說明ReentrantLock默認就是構(gòu)造一個非公平鎖涡贱,調(diào)用lock方法時候:

static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;

        /**
         * Performs lock.  Try immediate barge, backing up to normal
         * acquire on failure.
         */
        // Android-removed: @ReservedStackAccess from OpenJDK 9, not available on Android.
        // @ReservedStackAccess
        final void lock() {
      執(zhí)行CAS操作,本質(zhì)就是CAS更新state惹想,判斷state是否為0问词,
    如果為0則把0更新為1,并返回true否則返回false
            if (compareAndSetState(0, 1))

        //成功則將獨占鎖線程設(shè)置為當(dāng)前線程  
       
          setExclusiveOwnerThread(Thread.currentThread());
            else
            //否則再次請求同步狀態(tài)
                acquire(1);
        }

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

也就是說,通過CAS機制保證并發(fā)的情況下只有一個線程可以成功將state設(shè)置為1嘀粱,獲取到鎖激挪;此時,其他線程在執(zhí)行compareAndSetState時锋叨,因為state此時不是0垄分,所以會失敗并返回false,執(zhí)行acquire(1);

加入同步隊列:
   public final void acquire(int arg) {
    //再次嘗試獲取同步狀態(tài) 
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
 /**
         * Performs non-fair tryLock.  tryAcquire is implemented in
         * subclasses, but both need nonfair try for trylock method.
         */
        // Android-removed: @ReservedStackAccess from OpenJDK 9, not available on Android.
        // @ReservedStackAccess
        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState(); //獲取volatile類型的state
            if (c == 0) {
            //這里就是非公平鎖的訣竅:state為0表示當(dāng)前沒有線程獲取鎖,
          //此時不管條件隊列的順序娃磺,只要有線程獲取鎖失敗則會執(zhí)行CAS操作再次嘗試獲取一次.
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
          //這是表示獲取鎖的線程是Owner,也就是重入鎖線程加鎖的情況薄湿,
       //這時status會再次+1,因為是同一個線程所以不會有線程安全問題
            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;
        }

假設(shè)有三個線程:線程1已經(jīng)獲得了鎖豌鸡,線程2正在同步隊列中排隊嘿般,此時線程3執(zhí)行l(wèi)ock方法嘗試獲取鎖的時候,線程1正好釋放了鎖涯冠,將state更新為0炉奴,那么線程3就可能在線程2還沒有被喚醒之前去獲取到這個鎖。

如果此時還沒獲取到鎖蛇更,那么接下來就會把該線程封裝成node去同步隊列里排隊瞻赶,acquireQueued(addWaiter(Node.EXCLUSIVE), arg))

 /**
     * Creates and enqueues node for current thread and given mode.
     *
     * @param mode Node.EXCLUSIVE for exclusive, Node.SHARED for shared
     * @return the new node
     */
    private Node addWaiter(Node mode) {
        Node node = new Node(mode);
      //死循環(huán)
        for (;;) {
            Node oldTail = tail;
            if (oldTail != null) {
                U.putObject(node, Node.PREV, oldTail);
            //隊尾添加新結(jié)點
                if (compareAndSetTail(oldTail, node)) {
                    oldTail.next = node;
                    return node;
                }
            } else {
                initializeSyncQueue();
            }
        }
    }

  private final void initializeSyncQueue() {
        Node h;
        if (U.compareAndSwapObject(this, HEAD, null, (h = new Node())))
            tail = h;
    }
image.png

這個方法使用一個死循環(huán)進行CAS操作,可以解決多線程并發(fā)問題派任。假設(shè)線程1,2,3砸逊,4同時執(zhí)行addWaiter()方法入隊,而此時頭節(jié)點不為null掌逛,那么他們會同時執(zhí)行addWaiter中的compareAndSetTail方法將隊尾指向它师逸,添加到隊尾。但是這個時候CAS操作保證只有一個可以成功豆混,假設(shè)此時線程1成功添加到隊尾篓像,那么線程2,3,4執(zhí)行CAS都會失敗,方法內(nèi)部死循環(huán)保證所有線程直到成功添加到隊尾為止.

這里會涉及到兩個變化

  • 新的線程封裝成Node節(jié)點追加到同步隊列中皿伺,設(shè)置prev節(jié)點以及修改當(dāng)前節(jié)點的前置節(jié)點的next節(jié)點指向自己
  • 通過CAS講tail重新指向新的尾部節(jié)點
自旋

添加到同步隊列后员辩,結(jié)點就會進入一個自旋過程,即每個結(jié)點都在觀察時機待條件滿足獲取同步狀態(tài)鸵鸥,自旋過程是在acquireQueued方法中執(zhí)行的.

  final boolean acquireQueued(final Node node, int arg) {
        try {
            boolean interrupted = false;
            //死循環(huán)  自旋
            for (;;) {
            //獲取前置節(jié)點
                final Node p = node.predecessor();
            當(dāng)且僅當(dāng)p為頭結(jié)點才嘗試獲取同步狀態(tài)奠滑,此時符合FIFO的原則
                if (p == head && tryAcquire(arg)) {
                //將Node設(shè)置為頭結(jié)點
                    setHead(node);
                //清空原頭結(jié)點的引用便于GC
                    p.next = null; // help GC
                    return interrupted;
                }
              //如果前驅(qū)結(jié)點不是head,判斷是否掛起線程
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } catch (Throwable t) {
          //最終都沒能獲取到同步狀態(tài)  結(jié)束該線程的請求
            cancelAcquire(node);
            throw t;
        }
    }
    //設(shè)置頭結(jié)點
   private void setHead(Node node) {
        head = node;
        清空結(jié)點數(shù)據(jù) 以便于GC
        node.thread = null;
        node.prev = null;
    }

image.png

這個過程也是涉及到兩個變化

  • 修改head節(jié)點指向下一個獲得鎖的節(jié)點
  • 新的獲得鎖的節(jié)點,將prev的指針指向null
    這里有一個小的變化宋税,就是設(shè)置head節(jié)點不需要用CAS摊崭,原因是設(shè)置head節(jié)點是由獲得鎖的線程來完成的,而同步鎖只能由一個線程獲得弃甥,所以不需要CAS保證爽室,只需要把head節(jié)點設(shè)置為原首節(jié)點的后繼節(jié)點,并且斷開原h(huán)ead節(jié)點的next引用即可

當(dāng)然如果前驅(qū)結(jié)點不是head而它又沒有獲取到鎖淆攻,那么執(zhí)行如下:

 private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
    //獲取當(dāng)前結(jié)點的等待狀態(tài)
        int ws = pred.waitStatus;
    //如果是等待喚醒 狀態(tài) 則返回true
        if (ws == Node.SIGNAL)
            /*
             * This node has already set status asking a release
             * to signal it, so it can safely park.
             */
            return true;
    //如果ws>0 說明是結(jié)束狀態(tài)
//遍歷前驅(qū)結(jié)點直到找到?jīng)]有結(jié)束狀態(tài)的節(jié)點
        if (ws > 0) {
            /*
             * Predecessor was cancelled. Skip over predecessors and
             * indicate retry.
             */
//如果前節(jié)點被取消,則將取消節(jié)點移除隊列操作
            //找到當(dāng)前節(jié)點的前節(jié)點嘿架,如果前節(jié)點為取消節(jié)點則一直往前尋找一個節(jié)點瓶珊。pred.waitStatus > 0標(biāo)示已取消
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
      //如果ws小于0又不是SIGNAL狀態(tài)
//則將其設(shè)置為SIGNAL狀態(tài) 代表該節(jié)點的線程正在等待喚醒
            /*
             * 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.
             */
            pred.compareAndSetWaitStatus(ws, Node.SIGNAL);
        }
        return false;
    }


  private final boolean parkAndCheckInterrupt() {
      //將當(dāng)前線程掛起 線程會阻塞住
        LockSupport.park(this);
    //獲取線程中斷狀態(tài),interrupted是判斷當(dāng)前中斷狀態(tài) 
    //并非中斷線程 因此可能返回true也可能返回false
        return Thread.interrupted();
    }

這段代碼有個設(shè)計比較好的點:
通常我們在設(shè)計隊列時,我們需要考慮如何最大化的減少后續(xù)排隊節(jié)點對于CPU的消耗耸彪,而在AQS中伞芹,只要當(dāng)前節(jié)點的前驅(qū)結(jié)點不是頭節(jié)點,再把當(dāng)前節(jié)點加入到隊列后就會執(zhí)行LockSupport.park(this)蝉娜;將當(dāng)前線程掛起唱较,這樣可以最大程度減少CPU消耗.

AQS通過最簡單的CAS和LockSupport的park,設(shè)計出了高效的隊列模型和機制:
1.AQS結(jié)構(gòu)其實是在第二個線程獲取鎖的時候在初始化召川,就是lazy-init的思想南缓,最大限度減少不必要的代碼執(zhí)行的開銷
2.為了最大程度上提升效率,盡量避免線程間的通訊荧呐,采用了雙向鏈表的Node結(jié)構(gòu)去存儲線程
3.為了最大程度上避免CPU上下文切換執(zhí)行的消耗汉形,在設(shè)計排隊線程時,只有頭結(jié)點的下一個的線程一直在重復(fù)執(zhí)行后去鎖倍阐,隊列后面的線程會通過LockSupport進行休眠概疆。

非公平鎖的釋放
//ReentrantLock類的unlock方法
public void unlock() {
        sync.release(1);
    }

//AQS的release方法
  public final boolean release(int arg) {
//嘗試釋放鎖
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
          //喚醒后繼結(jié)點的線程
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

//ReentrantLock類中的內(nèi)部類Sync實現(xiàn)的tryRelease(int releases)方法
 protected final boolean tryRelease(int releases) {
            int c = getState() - releases;
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
//判斷狀態(tài)是否為0岔冀,如果是則說明已釋放同步狀態(tài)
            if (c == 0) {
                free = true;
            //設(shè)置Owner為null
                setExclusiveOwnerThread(null);
            }
      //設(shè)置更新同步狀態(tài)
            setState(c);
            return free;
        }

unParkSucessor(h)的作用喚醒后續(xù)的節(jié)點:

 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)
            node.compareAndSetWaitStatus(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 p = tail; p != node && p != null; p = p.prev)
                if (p.waitStatus <= 0)
                    s = p;
        }
        if (s != null)
            LockSupport.unpark(s.thread); //喚醒
    }

ReentrantLock中的公平鎖

與非公平鎖不同的是春锋,在獲取鎖的時候侧馅,公平鎖的獲取順序是完全遵循FIFO規(guī)則谊娇,也就是說先請求的線程一定會先獲取鎖小渊,后來的線程肯定需要排隊.
下面比較一下公平鎖和非公平鎖lock方法:

static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;

        /**
         * Performs lock.  Try immediate barge, backing up to normal
         * acquire on failure.
         */
        // Android-removed: @ReservedStackAccess from OpenJDK 9, not available on Android.
        // @ReservedStackAccess
    //非公平鎖:一上來二話不說直接CAS設(shè)置state搶鎖
        final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

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

公平鎖的lock方法:

 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.
         */
        // Android-removed: @ReservedStackAccess from OpenJDK 9, not available on Android.
        // @ReservedStackAccess
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
          //先判斷同步隊列是否存在節(jié)點
                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;
        }
    }

 public final boolean hasQueuedPredecessors() {
        // The correctness of this depends on head being initialized
        // before tail and on head.next being accurate if the current
        // thread is first in queue.
        Node t = tail; // Read fields in reverse initialization order
        Node h = head;
        Node s;
        return h != t &&
            ((s = h.next) == null || s.thread != Thread.currentThread());
    }

唯一的差別就是hasQueuedPredecessors()判斷同步隊列是否存在結(jié)點谬擦,這就是非公平鎖與公平鎖最大的區(qū)別,即公平鎖在線程請求到來時先會判斷同步隊列是否存在結(jié)點,如果存在先執(zhí)行同步隊列中的結(jié)點線程,當(dāng)前線程將封裝成node加入同步隊列等待虏冻。而非公平鎖呢鸥鹉,當(dāng)線程請求到來時灸异,不管同步隊列是否存在線程結(jié)點,直接嘗試獲取同步狀態(tài)褥傍,獲取成功直接訪問共享資源儡嘶,但請注意在絕大多數(shù)情況下,非公平鎖才是我們理想的選擇恍风,畢竟從效率上來說非公平鎖總是勝于公平鎖。

總結(jié):

以上便是ReentrantLock的內(nèi)部實現(xiàn)原理誓篱,這里我們簡單進行小結(jié)朋贬,重入鎖ReentrantLock,是一個基于AQS并發(fā)框架的并發(fā)控制類窜骄,其內(nèi)部實現(xiàn)了3個類锦募,分別是Sync、NoFairSync以及FairSync類邻遏,其中Sync繼承自AQS糠亩,實現(xiàn)了釋放鎖的模板方法tryRelease(int),而NoFairSync和FairSync都繼承自Sync准验,實現(xiàn)各種獲取鎖的方法tryAcquire(int)

AQS在設(shè)計時將性能優(yōu)化到了極致赎线,具體體現(xiàn)在同步隊列的park和unpark,初始化AQS時的懶加載糊饱,以及線程之間通過Node這樣的數(shù)據(jù)結(jié)構(gòu)從而避免線程間通訊造成的額外開銷垂寥,這種由釋放鎖的線程主動喚醒后續(xù)線程的方式也是我們再實際過程中可以借鑒的.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市另锋,隨后出現(xiàn)的幾起案子滞项,更是在濱河造成了極大的恐慌,老刑警劉巖夭坪,帶你破解...
    沈念sama閱讀 218,122評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件文判,死亡現(xiàn)場離奇詭異,居然都是意外死亡室梅,警方通過查閱死者的電腦和手機戏仓,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評論 3 395
  • 文/潘曉璐 我一進店門疚宇,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人柜去,你說我怎么就攤上這事灰嫉。” “怎么了嗓奢?”我有些...
    開封第一講書人閱讀 164,491評論 0 354
  • 文/不壞的土叔 我叫張陵讼撒,是天一觀的道長。 經(jīng)常有香客問我股耽,道長根盒,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,636評論 1 293
  • 正文 為了忘掉前任物蝙,我火速辦了婚禮炎滞,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘诬乞。我一直安慰自己册赛,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,676評論 6 392
  • 文/花漫 我一把揭開白布震嫉。 她就那樣靜靜地躺著森瘪,像睡著了一般。 火紅的嫁衣襯著肌膚如雪票堵。 梳的紋絲不亂的頭發(fā)上扼睬,一...
    開封第一講書人閱讀 51,541評論 1 305
  • 那天,我揣著相機與錄音悴势,去河邊找鬼窗宇。 笑死,一個胖子當(dāng)著我的面吹牛特纤,可吹牛的內(nèi)容都是我干的军俊。 我是一名探鬼主播,決...
    沈念sama閱讀 40,292評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼叫潦,長吁一口氣:“原來是場噩夢啊……” “哼蝇完!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起矗蕊,我...
    開封第一講書人閱讀 39,211評論 0 276
  • 序言:老撾萬榮一對情侶失蹤短蜕,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后傻咖,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體朋魔,經(jīng)...
    沈念sama閱讀 45,655評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,846評論 3 336
  • 正文 我和宋清朗相戀三年卿操,在試婚紗的時候發(fā)現(xiàn)自己被綠了警检。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片孙援。...
    茶點故事閱讀 39,965評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖扇雕,靈堂內(nèi)的尸體忽然破棺而出拓售,到底是詐尸還是另有隱情,我是刑警寧澤镶奉,帶...
    沈念sama閱讀 35,684評論 5 347
  • 正文 年R本政府宣布础淤,位于F島的核電站,受9級特大地震影響哨苛,放射性物質(zhì)發(fā)生泄漏鸽凶。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,295評論 3 329
  • 文/蒙蒙 一建峭、第九天 我趴在偏房一處隱蔽的房頂上張望玻侥。 院中可真熱鬧贷岸,春花似錦椿浓、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽票摇。三九已至,卻和暖如春砚蓬,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背盆色。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評論 1 269
  • 我被黑心中介騙來泰國打工灰蛙, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人隔躲。 一個月前我還...
    沈念sama閱讀 48,126評論 3 370
  • 正文 我出身青樓摩梧,卻偏偏與公主長得像,于是被迫代替她去往敵國和親宣旱。 傳聞我的和親對象是個殘疾皇子仅父,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,914評論 2 355