ReentrantLock読解


ReentrantLock
ReentrantLockは反発ロックであり、再入ロックでもある.ReentrantLockロックは、同じ時点で1つのスレッドによってのみ保持されますが、1つのスレッドによって複数回取得され、AQSのstateを取得するたびに1が加算されます.ReentrantLock内部の実装(フェアロックと非フェアロック)は、内部Syncに基づく実装である.
ないぶこうぞう
ReentrantLock内部には、3つの重要な内部クラス、Sync、FairSync、NonfairSyncが定義されています.
Syncは、抽象クラスAbstractQueuedSynchronizer、FairSync(パブリックロック)、NonfairSync(非フェアロック)からSyncに継承されます.
Sync
abstract static class Sync extends AbstractQueuedSynchronizer {
    private static final long serialVersionUID = -5179523762034025860L;

    abstract void lock();
    //     TryAcquire
    final boolean nonfairTryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();
        if (c == 0) {//         
            if (compareAndSetState(0, acquires)) {//CAS   
                setExclusiveOwnerThread(current);//    ,        Owner
                return true;
            }
        }
        else if (current == getExclusiveOwnerThread()) {//          , state+1  
            int nextc = c + acquires;
            if (nextc < 0) // overflow
                throw new Error("Maximum lock count exceeded");
            setState(nextc);
            return true;
        }
        return false;
    }

    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;
    }
    //             
    protected final boolean isHeldExclusively() {
        return getExclusiveOwnerThread() == Thread.currentThread();
    }
    //newCondition
    final ConditionObject newCondition() {
        return new ConditionObject();
    }
    //      
    final Thread getOwner() {
        return getState() == 0 ? null : getExclusiveOwnerThread();
    }

    final int getHoldCount() {
        return isHeldExclusively() ? getState() : 0;
    }

    final boolean isLocked() {
        return getState() != 0;
    }

    private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        s.defaultReadObject();
        setState(0); // reset to unlocked state
    }
}

SyncはReentrantLockが同期制御を実現する基礎である.Sync内部では、lock(サブクラスで実装されたロックを取得する方法)、nonfairTryAcquire(非公平ロックの試みによるリソースの取得)、tryRelease(リソースの解放)のいくつかの方法が定義されています.
NonfairSync
static final class NonfairSync extends Sync {
    private static final long serialVersionUID = 7316153563782823691L;
    
    final void lock() {
        if (compareAndSetState(0, 1))
            setExclusiveOwnerThread(Thread.currentThread());
        else
            acquire(1);
    }
    
    protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires);
    }
}

NonfairSyncは内部の非公平ロックの定義であり、非公平ロックがロックを取得するプロセスです.
  • lockメソッドが呼び出され、lockメソッドはまずCAS操作を行い、stateプロパティを1に設定しようとし、成功した場合はロックの取得を表し、exclusiveOwnerThreadプロパティを現在のロックの取得スレッドに設定します.
  • スレッドCASが失敗した場合、AQSのacquireメソッドを呼び出してロックを取得し、tryAcquire(1)はサブクラス独自の実装であり、nonfairTryAcquireメソッドが呼び出される.nonfairTryAcquireメソッドを呼び出してロックを取得できなかった場合、現在のスレッドパッケージはNodeに同期キューに追加されます.

  • FairSync
    static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;
    
        final void lock() {
            acquire(1);
        }
        //tryAcquire        ,      ,           ,                         
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                //hasQueuedPredecessors                  ,       ,        
                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;
        }
    }
    

    フェアロックロックロックロックロックはロックをかけるときに先にロックをかけることを試みず、AQSのacquireメソッドを直接呼び出してロックを取得し、自分で定義した試行リソースでstateが0であれば、現在のスレッドの前にスレッドが待っているかどうかを判断し、待っているスレッドがなければ自分でロックを取得し、そうでなければ待機キューに参加する.現在のスレッドがロックを取得する場合state+1.
    フェアロックと非フェアロックの違い
  • フェアロックは、古いスレッドがキューに並んでロックを使用し、新しいスレッドがキューに並んでロックを使用することを保証します.
  • 非公平ロック保証:古いスレッドが並んでロックを使用する.しかし、新しいスレッドがキューに並んでいるスレッドのロックを優先することは保証されません.