ReentrantLockのソースの分析

構造

SyncがReentrantLockの内部静的抽象クラスであり、以下に示すReentrantLockの構造は、AQSから継承します。FairSyncとNonfairSync相続同期。

画像-20190421141918076
パターンテンプレートを使用してAQS法、鋳型法は、抽象クラスで全体的又は全体的なアルゴリズムの動作を一緒に形成する方法の基本的な動作を定義しています。抽象クラスは1つに限定されないテンプレートメソッド、任意の数を持つことができます。各テンプレートの方法は、特定の任意の数の方法を呼び出すことができます。この方法は、acquireShared、放出、AQSは、取得テンプレートを有する等releaseShared、テンプレートは、特定のメソッド法(フック)呼び出しサブクラスの実装によって達成されます。AQSフックメソッドが定義されました:

  • tryAcquire
  • tryRelease
  • tryAcquireShared
  • tryReleaseShared
  • isHeldExclusively

フックメソッドは、完全な書き換えを必要としない、書き換えはUnsupportedOperationExceptionをスローデフォルトでメソッドをフックしませんでした。

画像-20190421145309101

ロック

デフォルト以外の公正ロックを指定しない場合は、公正の使用を指定することができReentrantLokコンストラクタ。いわゆるフェアは、非資本がすぐにあるとそれをつかむためにロックを取得しようとしたときに、仕分けロック待機時間を押して、つかむ再びキューを取得することです。デフォルト以外のフェアのいくつかの高効率ので、不正利用。しかし、不公平なスレッドがロックを取得することはできません他のスレッドにリードをつかむのに長い時間のためにロックされていることを、飢餓をスレッドにつながる可能性があります。

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

public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
}
复制代码
//ReentrantLock的lock方法
public void lock() {
        sync.lock();
		}
//NoneFairSycn的lock实现
 final void lock() {
   			// 先立即尝试一下加锁(非公平)
        if (compareAndSetState(0, 1))
            setExclusiveOwnerThread(Thread.currentThread());
        else
          //获取不到再去acquire
            acquire(1)
    }
//compareAndSetState通过unsafe的CAS实现(jdk9之后用的是VarHandler)
protected final boolean compareAndSetState(int expect, int update) {
        // See below for intrinsics setup to support this
        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
    }
    
复制代码

テンプレートメソッドAQSが取得されるロック方法ReentrantLockの最後の呼び出し、メソッドを取得あまりを取得し、ロックを取得するためにtryAcquireの試みを呼び出すには(falseを返す)、その後、再利用acquireQueue方法は、キューに入れられました。中断しないselfInterrupt()は、割り込みが)、次のセットをマークするだけacquireInterruptiblyを使用しての応答時間を中断しますが、その方法はここにあることが、doAcquireInterruptiblyを(コール転送されないことに注意してください

public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
      //acquireQueued这个方法会block住,如果block的过程中被中断了会返回true,此时if成立 执行selfInterrupt(Thread.interrupt,不会抛异常)
        selfInterrupt();
}
复制代码

tryAcquireが達成アンフェアロック:

//NonfairSync 中的tryAcquire实现
protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }

final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
  //state为0说明没有线程占用锁
    if (c == 0) {
      //直接cas尝试占用
        if (compareAndSetState(0, acquires)) {
          //exclusiveOwnerThread是非volatile的,其getter和setter也是未加锁的
          //所以使用时要确认不会有其他线程尝试set
          //这里(compareAndSetState(0, acquires)保证只有当前线程操作
            setExclusiveOwnerThread(current);
            return true;
        }
    }
  //如果锁的当前持有者就是当前线程,那么就直接获取(state+1),实现了ReentrantLock的可重入
    else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
  //都不满足抢占锁失败,返回false
    return false;
}
复制代码

addWaiter現在のスレッドは、Nodeオブジェクトとしてパッケージされ、同期キュー(ノードの実際リスト)を添加

private Node addWaiter(Node mode) {
				//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;
           	//先快速尝试一遍把节点插到尾部,cas失败的话再使用enq方法插入
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
  			//enq就是在自旋锁里使用cas插到队列尾部
        enq(node);
        return node;
    }
复制代码

ノード構成とwaitstatus:

 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;
				//在同步队列中等待的线程等待超时或者被中断,需要从同步队列中取消等待。
        static final int CANCELLED =  1;
				//后继节点处于等待状态,当前节点如果释放了同步状态或者被取消,
				//将会通知后继节点,使后继节点得以运行.
        static final int SIGNAL    = -1;
        //节点在等待队列中,等其他线程调用了condition的signal之后
        //该节点将会从等待队列中转移到同步队列中
        static final int CONDITION = -2;
        /**
         * waitStatus value to indicate the next acquireShared should
         * unconditionally propagate
         */
        static final int PROPAGATE = -3;
				//初始状态0
        volatile int waitStatus;
				//前节点
        volatile Node prev;
				//后节点
        volatile Node next;
      	//当前节点装载的线程
        volatile Thread thread;
				//等待队列中的后继节点,因为只有独占锁才有等待队列所以如果当前节点是共享的
				//复用该字段作为节点类型标记(常量SHARED). (我个人很讨厌这种骚操作)
        Node nextWaiter;

        /**
         * Returns true if node is waiting in shared mode.
         */
        final boolean isShared() {
            return nextWaiter == SHARED;
        }

        /**
         * Returns previous node, or throws NullPointerException if null.
         * Use when predecessor cannot be null.  The null check could
         * be elided, but is present to help the VM.
         *
         * @return the predecessor of this node
         */
        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
        }

        Node(Thread thread, Node mode) {     // Used by addWaiter
            this.nextWaiter = mode;
            this.thread = thread;
        }

        Node(Thread thread, int waitStatus) { // Used by Condition
            this.waitStatus = waitStatus;
            this.thread = thread;
        }
    }
复制代码

スピンロック+公園(停滞)によるAQSのacquireQueued方法、同期キューノードを挿入する方法

ここtryAcquireは、前駆体ノードが現在のノードが早期にウェイクアップされ(または先行ノードがparkSuccessorを引き起こす中断cancelAcquire)場合、同期FIFOキューを確保し、最初のノード、であるかどうかを決定します前に、唯一の前駆体ノードは、時間の頭を取得しようとしますですロック

final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
        boolean interrupted = false;
        //这里用一个死循环自旋
        for (;;) {
        		//p是当前节点的前驱节点
            final Node p = node.predecessor();
            //如果前置节点是头结点(即当前节点是下一个要获取锁的节点),再去尝试获取锁
            if (p == head && tryAcquire(arg)) {
            //将当前节点置为头结点
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return interrupted;
            }
          //短路操作,尝试获取锁失败之后判断是否可以park
          //可以park,调用parkAndCheckInterrupt方法park当前线程
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
              //骚写法,parkAndCheckInterrupt()返回true时才能执行到这里,说明有中断
                interrupted = true;
        }
    } finally {
      //如果在tryAcquire成功之前异常了,取消acquire,删除当前节点
        if (failed)
            cancelAcquire(node);
    }
}
复制代码

次のようにshouldParkAfterFailedAcquire公園は、現在のプロセスかどうかを決定することができます。

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
    int ws = pred.waitStatus;
  	//如果前驱节点waitStatus是signal,前驱节点释放了后会通知当前node,可以直接park
    if (ws == Node.SIGNAL)
        /*
         * This node has already set status asking a release
         * to signal it, so it can safely park.
         */
        return true;
  //唯一大于0的状态就是cancel,前驱节点状态为cancel说明前驱节点被中断或超时了,那么向前继续寻找
  //知道找到状态>=0的节点,插在该节点后面
    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 {
      //第一次调用这个方法时都会进到这个分支,因为前驱节点的状态为0.然后回到自旋流程
        /*
         * 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.
         */
      //cas将前驱接节点状态设置为signal
        compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
    }
  //前驱节点不为signal,继续自旋
    return false;
}
复制代码

公園・ブロック、現在のスレッド、またはunparkを呼び出すには時間がスレッドがThread.interruptedによって中断されているかどうかを確認するために、リターン()の後、公園メソッドから返されます中断します。

private final boolean parkAndCheckInterrupt() {
    LockSupport.park(this);
    return Thread.interrupted();
}

复制代码

アンロック(解除)

unlockメソッド呼び出しReentrantLockの通過ロックを解除するには

public void unlock() {
    sync.release(1);
}

复制代码

テンプレート方式でAQS放出は、最初の状態が0まで減少し、その後unparkSuccessor unparkを後続ノードを呼び出しているtryRelease呼ばれます。

同期キューリストを行うには操作はありませんが、リンクリストの置換の最初のノードはacquireQueue作られています

public final boolean release(int arg) {
  //调用reentrantLock里的tryRelease具体实现
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
          //unpark后继节点
            unparkSuccessor(h);
        return true;
    }
    return false;
}
复制代码

java.util.concurrent.locks.ReentrantLock.Sync#tryRelease:

protected final boolean tryRelease(int releases) {
		//state release一次减1(可能重入过多次)
    int c = getState() - releases;
  	//未拿到锁的线程不能释放其他线程的锁
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
  	//state减到0释放锁
    if (c == 0) {
        free = true;
        setExclusiveOwnerThread(null);
    }
    setState(c);
    return free;
}
复制代码

ロックunparkを後続ノードjava.util.concurrent.locks.AbstractQueuedSynchronizer#unparkSuccessorのリリース後:

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;
  //如果后继节点为null或者被cancel了 从尾部往前找,node后面的第一个ws小于0的节点
  //然后unpark这个节点
    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);
}
复制代码

調子

オブジェクトは、メソッドを通知/条件と同様のモニター待機を提供することです

ConditionObjectのはConditon#1粟井スレッドが現在の方法は、現在のスレッドの構成ノードとなる呼び出す。各オブジェクトは、状態キューおよび同期キュー多重化ノードオブジェクトを含む。AQS内部クラスは、インターフェイスを実装conditonであり、ノード挿入待ち行列尾。

シンクロナイザは、同期キューへの複数の参照を持っているし、キューを待って、待ち行列は、同期キューを保持して待機しています。

画像-20190423155406586

ReentrantLockののnewCondition法で条件を作成します。

//java.util.concurrent.locks.ReentrantLock#newCondition
public Condition newCondition() {
    return sync.newCondition();
}
//java.util.concurrent.locks.ReentrantLock.Sync#newCondition
final ConditionObject newCondition() {
            return new ConditionObject();
        }
复制代码

使用例を参照してください。

@Test
public void testAQSCondition() {
    ReentrantLock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
    Thread threadA = new Thread(() -> {
      //先获取锁
        lock.lock();
        condition.signalAll();
        System.out.println("threadA signal all");
        try {
            Thread.sleep(5 * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println("threadA unlock");
          //释放锁
            lock.unlock();
        }
    });

    lock.lock();
    threadA.start();
    try {
      //等待并释放锁
        condition.await();
        System.out.println("wake from await");
    } catch (InterruptedException e) {
        e.printStackTrace();
    } finally {
        lock.unlock();
    }
}

input:
threadA signal all
threadA unlock
wake from await
复制代码

また、メインスレッドが時から待機を返すsignalAllロック.threadAコールを取得するスレッドAの後。次に、上記の例では、ロックを得るのawaitメソッドブロックをロックを解放する現在のスレッドを呼び出すためにのみメインスレッド後にロックを解除します我々は非常によく似た通知/待ってロックを確認し、監視することができます。

public final void await() throws InterruptedException {
  //如果当前线程被中断了抛出异常
    if (Thread.interrupted())
        throw new InterruptedException();
  //新建一个Node,添加到等待队列
    Node node = addConditionWaiter();
  //释放当前锁
    int savedState = fullyRelease(node);
    int interruptMode = 0;
  //自旋,确定node转移到同步队列退出
    while (!isOnSyncQueue(node)) {
      //park阻塞
        LockSupport.park(this);
      //唤醒之后如果有中断退出循环
        if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
            break;
    }
  //先插入同步队列 获得锁之后再判断中断类型
    if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
        interruptMode = REINTERRUPT;
    if (node.nextWaiter != null) // clean up if cancelled
        unlinkCancelledWaiters();
  //根据中断模式处理中断(抛异常或者Thread.interrupt)
    if (interruptMode != 0)
        reportInterruptAfterWait(interruptMode);
}
复制代码

SharedLock

ReentrantReadWriteLockは、それぞれ読み書きロックとreadLockを通じて取得WRITELOCK

public ReentrantReadWriteLock.WriteLock writeLock() { return writerLock; }
public ReentrantReadWriteLock.ReadLock  readLock()  { return readerLock; }
复制代码

このようなものは、袋の内側に2つの静的内部クラス、書き込みロックReadLockであり、二つのインタフェースは、同期フィールドAQSの実現と、ロックを実現している。WiteLockが排他ロックである、readlockが共有ロックです。

読み書きロックなどReentrantLockのと公平性の選択肢を提供するには、再入可能です。

揮発性読み書きロックは、書き込み状態を示すために使用される下位16ビット、読み出し状態16を示すように高く、ロック状態ビットセグメンテーションの状態を表すために使用されます。

読み取りロックがある場合は、書き込みロックを取得することができ、書き込み動作は、読み取り操作に見えるはずですので、すべての読み取りロックのみ書き込みロックを取得するために入れています

別のスレッドが取得した場合、書き込みロック現在のスレッドが読み取りロックを取得することができないだけで書き込みロックを取得したり、ロックを読むために、現在のスレッドは読み取りロックを取得するための任意のスレッドが取得されていません

私はそれが掲示言う時間を持っていないためにコードを記述する気にしたくありません

おすすめ

転載: juejin.im/post/5d5fa985f265da03c927026b