Java鎖之ReentrantLock(一)

木木匠發表於2018-08-10

一、ReenTrantLock結構

Java鎖之ReentrantLock(一)

圖1-1

根據上圖可以知道,ReenTrantLock繼承了Lock介面,Lock介面宣告方法如下:

方法名 說明 丟擲異常
lock() 一直阻塞獲取鎖,直到獲取成功
lockInterruptibly() 嘗試獲取鎖,直到獲取鎖或者執行緒被中斷 InterruptedException
tryLock() 嘗試獲取空閒的鎖,獲取成功返回true,獲取失敗返回false,不會阻塞,立即返回
tryLock(long time, TimeUnit unit) 嘗試在time時間內獲取空閒的鎖,在等待時間內可以被中斷 InterruptedException
unlock() 釋放鎖
newCondition() 返回當前鎖的一個condition例項,用於條件性等待

二、Lock的實現類ReentrantLock

1.ReentrantLock的部分方法

Java鎖之ReentrantLock(一)

圖2-1

  • 根據圖2-1可以知道 Sync物件提供了所有的實現機制,而Sync繼承了AbstractQueuedSynchronizer
  • NonfairSync 不公平鎖,繼承了Sync
  • FairSync 公平同步,繼承了Sync

1. Sync


Sync是NonfairSync 和FairSync 的父類,宣告方法如下:

      /**
       * 抽象方法,獲取鎖
       */
      abstract void lock();

      /**
       * 實現非公平鎖獲取邏輯
       */
      final boolean  nonfairTryAcquire(int acquires) {
          final Thread current = Thread.currentThread();
          int c = getState(); //父類同步器方法,獲取當前同步狀態,後續文章會分析
          if (c == 0) {//狀態等於0表示沒有獲取到鎖
              if (compareAndSetState(0, acquires)) { //CAS方式修改狀態
                  setExclusiveOwnerThread(current); //修改成功後設定當前執行緒為鎖的所有者
                  return true;
              }
          }
          else if (current == getExclusiveOwnerThread()) {//當前鎖已被佔用,判斷是不是自己獲取到了鎖,鎖重入
              int nextc = c + acquires; //獲取鎖的計數器
              if (nextc < 0) // overflow //因為是int型別,如果超過int最大值會溢位為負
                  throw new Error("Maximum lock count exceeded");
              setState(nextc);//設定計數器為狀態值
              return true;
          }
          return false;
      }

      protected final boolean tryRelease(int releases) {
          int c = getState() - releases;//釋放鎖,同步狀態減int值
          if (Thread.currentThread() != getExclusiveOwnerThread())
              throw new IllegalMonitorStateException(); //如果當前相差不是鎖的擁有者,丟擲異常
          boolean free = false;
          if (c == 0) { //如果同步狀態值為0,表示鎖已經釋放成功
              free = true; 
              setExclusiveOwnerThread(null); // 設定鎖的擁有執行緒為null
          }
          setState(c);//重新賦值同步狀態
          return free;
      }
      //判斷當前執行緒是不是鎖獨佔
      protected final boolean isHeldExclusively() {
        
          return getExclusiveOwnerThread() == Thread.currentThread();
      }
      //返回鎖的ConditionObject例項
      final ConditionObject newCondition() {
          return new ConditionObject();
      }

      // Methods relayed from outer class
      //獲取當前佔有鎖的執行緒
      final Thread getOwner() {
          return getState() == 0 ? null : getExclusiveOwnerThread();
      }
      //獲取當前鎖計數
      final int getHoldCount() {
          return isHeldExclusively() ? getState() : 0;
      }
      //判斷是否獲取到鎖
      final boolean isLocked() {
          return getState() != 0; //可以知道判斷獲取鎖的關鍵就是是否不等於0
      }
複製程式碼

2. NonfairSync 非公平鎖


 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() {
           if (compareAndSetState(0, 1))//CAS獲取鎖
               setExclusiveOwnerThread(Thread.currentThread());
           else
               acquire(1);
       }

       protected final boolean tryAcquire(int acquires) {
           return nonfairTryAcquire(acquires);
       }
   }
複製程式碼
  • 根據原始碼發現 非公平鎖繼承了Sync父類,由於鎖的釋放不存在公平與不公平,所以公平鎖和非公平鎖只實現各自獲取鎖的邏輯。根據非公平鎖的原始碼發現,其內部只實現了lock()tryAcquire(int acquires)方法,其中和tryAcquire(int acquires)方法直接呼叫了父類的nonfairTryAcquire(acquires),介紹父類的時候已經解析過,不清楚可以看上文Sync解析部分。根據lock原始碼發現,開始判斷是否是第一次獲取鎖,如果獲取鎖成功,就把當前執行緒設定為鎖的佔有者,否則呼叫父類的acquire(1)方法(下一篇介紹同步器會介紹)。

3. FairSync 公平鎖


 static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;
        /**呼叫父類的acquire()方法*/
        final void lock() {
            acquire(1);
        }

        /**
         * 嘗試獲取鎖
         */
        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;
        }
    }
複製程式碼
  • 根據上面程式碼發現公平鎖的獲取程式碼和非公平鎖獲取鎖程式碼很相識,公平鎖只多了一個 !hasQueuedPredecessors()判斷,其實該方法就是判斷是否有比當前執行緒等待最長時間的執行緒,如果沒有,那麼就嘗試獲取鎖,獲取成功後設定當前執行緒為鎖的佔有者,所以,公平與不公平就是是否按照時間等待來獲取鎖的,比如食堂吃飯,排隊一個個來,這就是公平,如果有人插隊,這就是不公平。

3. ReentrantLock 其他方法


 public ReentrantLock() {
        sync = new NonfairSync();
    }
public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }
  public void lock() {
        sync.lock();
    }
 public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);
    }
public boolean tryLock() {
        return sync.nonfairTryAcquire(1);
    }
    //指定超時時間內獲取鎖,阻塞時間為timeout
 public boolean tryLock(long timeout, TimeUnit unit)
            throws InterruptedException {
        return sync.tryAcquireNanos(1, unit.toNanos(timeout));
    }
 public Condition newCondition() {
        return sync.newCondition();
    }
public int getHoldCount() {
        return sync.getHoldCount();
    }
  public boolean isHeldByCurrentThread() {
        return sync.isHeldExclusively();
    }
 public final boolean isFair() {
        return sync instanceof FairSync;
    }
protected Thread getOwner() {
        return sync.getOwner();
    }
public final boolean hasQueuedThreads() {
        return sync.hasQueuedThreads();
    }
  public final boolean hasQueuedThread(Thread thread) {
        return sync.isQueued(thread);
    }
public final int getQueueLength() {
        return sync.getQueueLength();
    }
protected Collection<Thread> getQueuedThreads() {
        return sync.getQueuedThreads();
    }
 public boolean hasWaiters(Condition condition) {
        if (condition == null)
            throw new NullPointerException();
        if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
            throw new IllegalArgumentException("not owner");
        return sync.hasWaiters((AbstractQueuedSynchronizer.ConditionObject)condition);
    }
   public int getWaitQueueLength(Condition condition) {
        if (condition == null)
            throw new NullPointerException();
        if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
            throw new IllegalArgumentException("not owner");
        return sync.getWaitQueueLength((AbstractQueuedSynchronizer.ConditionObject)condition);
    }
    protected Collection<Thread> getWaitingThreads(Condition condition) {
        if (condition == null)
            throw new NullPointerException();
        if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
            throw new IllegalArgumentException("not owner");
        return sync.getWaitingThreads((AbstractQueuedSynchronizer.ConditionObject)condition);
    }

複製程式碼
  • 其實根據上面原始碼發現,不管是實現LOCK介面的方法,還是後續新增的方法,其實現功能都依託於一個物件,那就是sync,在介紹sync時候已經說過,它就是繼承了AbstractQueuedSynchronizer同步器,很多方法都是直接呼叫父類同步器的方法,下一篇《java鎖之ReentrantLock(二)》會重點解析AbstractQueuedSynchronizer同步器原始碼,分析同步器是如何依託於FIFO佇列完成鎖的機制。

三、 總結

  • ReentrantLock實現了LOCK介面
  • ReentrantLock可以實現公平鎖和非公平鎖獲取
  • ReentrantLock可以進行超時時間內獲取鎖
  • ReentrantLock可以進行條件等待和喚醒
  • ReentrantLock可以獲取鎖響應中斷

相關文章