쓰기 소스 코드를 잠금 ReentrantReadWriteLock에

1. 소개

동시 일반적으로 사용 ReentrantLock와는 전형적인 전용 잠금 이러한 잠금은 실제로 직렬 동작으로 동시에 작동 동시에 방문에 하나 개의 스레드 만이 허용된다. 비즈니스의 동시 다량에서, 전반적인 효율, 처리량이 구현의 요구를 충족시킬 수 없다. 그리고 일반적으로 실제 비즈니스 쓰기보다 읽기, 이미 존재하는 데이터를 변경하지 않는 하나 개 이상의 스레드를 읽고, 다른 사람에 작업을 읽고,이 데이터의 일관성이 없으며, 데이터 쓰기 작업이 변경됩니다 당신은 오래된 데이터를 읽을 수 있습니다. 액세스 읽기에 동시에 그것은 발생이 읽기 - 쓰기 잠금 비즈니스 요구 사항입니다, 읽기 - 쓰기 잠금은 여러 스레드를 허용하지만, 스레드에 대한 쓰기 액세스는 모든 스레드를 읽고 다른 스레드가 차단되는 물품. 읽기 - 쓰기 잠금 일반 배타적 잠금이 크게 향상되었습니다에 비해 동시성을 만들고, 분리 및 쓰기 잠금 읽을 잠금, 잠금 및 읽기 - 쓰기 잠금을 유지합니다.

쓰기 잠금 지원 공정하고 불공정 잠금 래치, 잠금 박람회 기본이 아닌 구현, 특정 잠금 래치에 비해 비 공정 박람회.

지지체는 읽기 및 쓰기 재진입, 최대 읽기 및 쓰기 잠금 오목 번호 자체 결정 부분의 범위를 int 형의 표현 인 65535 있고 잠금.

읽기 쓰기 잠금 제에 따른 쓰기 잠금을 획득하고 잠금 동작을 수행하는 기록 잠금 해제 명령에 대한 판독 로크를 얻고, 기입 로크는 판독 로크 다운 그레이드 될 수 록 강등을 달성 할 수있다.

2.ReentrantReadWriteLock 클래스 구조

알 수있는 바와 같이도. ReentrantReadWriteLock UML 내부 클래스 복수 다소 유사한 이러한 클래스 및 내측 ReentrantLock와에서, AQS 동기화 이전로부터 상속 동기화 NofairSync, FairSync을 가지고, 전 동기화 NofairSync, FairSync 상위 클래스이다.

 

 

 

, ReentrantLock와 비교하여 동일한 세 정적 내부 클래스 이름하지만, 그러나 내 차이가있다. ReentrantLock와의 배타적 로크이지만, 또한 기입 로크라고 할 수 있고, 고정 세 내부 클래스 전용 된 tryAcquire에서 AQS (INT) tryRelease (INT) 개의 배타적 로크 관련된 방법 재정의. ReentrantReadWriteLock 독점 잠금을 달성하기 위해, 잠금 읽기 - 쓰기되고 공유 잠금이 모두 잠금, 동기화, NofairSync, 공정뿐만 아니라 SynctryAcquire (int)를 재 작성, tryRelease (int) 메소드, tryAcquireShared에서 AQS (int)를 재 작성하는 것 외에도 , tryReleaseShared (int)에 연결된 두 방법 공유 잠금.

ReadLock 내부 클래스는 정적 잠금, 주로 "무효 획득 (INT)" "부울 릴리스 (INT)"와 (이들 두 방법처럼 같이 NofairSync / FairSync이 로크 방법을 구현 W 연관된 공유 한 위탁 판독 부모 클래스 AQS이다 템플릿 방법, 템플릿 메소드 호출 자체가 다시 된 tryAcquire (int)를, tryRelease (int) 메소드를) 다시.

writeLock를 정적 내부 클래스는 "무효 acquireShared (INT)" "부울 releaseShared (INT)"등등과 같은 달성하는 방법, 연관된 전용 자물쇠 잠금이 주로 위탁 NofairSync / FairSync 물품이다.

 

HoldCounter 동기화 및 ThreadLocalHoldCounter 정적 내부 클래스는, 주요 역할은 HoldCounter 클래스는 공유 읽기 잠금 스레드를 얻을 수 재진입 테이크의 숫자를 원 기록하는 것입니다, ThreadLocalHoldCounter는 주로 리더 스레드가 별도로 관리 취할 각 공유 잠금을 획득 한 HoldCounter.

 

 

이 방법의 일례 3.ReadWriteLock

하는 ReadWriteLock 인터페이스는 두 개의 추상 메소드, 각각 읽기 잠금을 획득하고 잠금을 읽을 수 있습니다.
공용  인터페이스 하는 ReadWriteLock는 {
     / ** 
     * 읽기에 사용하는 락을 돌려줍니다. 
     * 
     * @return 읽기에 사용하는 락을
      * / 
    잠금 readLock (); 

    / ** 
     * 작성에 사용하는 락을 돌려줍니다. 
     * 
     * @return 작성에 사용하는 락을
      * / 
    잠금 writeLock를 (); 
}

 

 

 

또한 ReentrantReadWriteLock 일부 유형을 직접 얻기 위해 동기화 (또는 서브 NofairSync / FairSync)를 위임 일부 보조 동시 프로그래밍 방법을 더욱 쉽게 만든다.

    공공  INT getReadLockCount () {
         반환 sync.getReadLockCount (); 
    } 

    공공  부울은 {() isWriteLocked
         반환 ) (sync.isWriteLocked; 
    } 
  
    공공  부울 isWriteLockedByCurrentThread () {
         반환 ) sync.isHeldExclusively을 (; 
    } 
 
    공공  INT getWriteHoldCount () {
         반환 sync.getWriteHoldCount (); 
    } 
 
    공공  INT getReadHoldCount () {
         반환 sync.getReadHoldCount (); 
    }

 

 getReadLockCount는 () 스레드의 수는 성공적으로 잠금을 보유 읽기 잠금의 수 읽기 잠금을 획득 돌려줍니다. .

쓰기 잠금은 스레드를 성공적으로 획득 한 ()되어 반환 isWriteLocked.

isWriteLockedByCurrentThread ()는 쓰기 잠금이 성공적으로 현재의 thread를 획득 반환합니다.

getWriteHoldCount는 () 횟수가 현재의 thread가 반복적으로의 쓰기 잠금 (재입국)을 취득 돌려줍니다.

getReadHoldCount ()는 반복해서 취득 된 현재 스레드의 판독 자물쇠 (오목)의 개수를인가 반환한다.

 

4. 저장 읽기 - 쓰기 디자인

상태 하나 마이너스 1 (의 상태의 상태로 될 때마다 재 방출하여 증가되면 int 타입의 멤버 변수의 상태 AQS ReentrantLock와 사용은 단독 잠금 상태 재진입을 길게 이전 기사 ). ReentrantReadWriteLock의 재입국은이 디자인 개념을 기반으로해야한다. ReentrantLock와 만 상태 멤버 변수의 직접 사용을 달성 할 수 ReentrantReadWriteLock을 저장하는 방법의 핵심 거짓말을 동시에 읽고 잠금 상태의 쓰기 잠금 상태 쓰기 잠금 상태를 저장해야합니다. 사실, 플래그 레지스터 CPU, 변수 "비트 분할", 다른 의미의 서로 다른 비트 범위의 디자인 컨셉에서 배울 수 있습니다. 16 비트의 상태를 판독하여 ReentrantReadWriteLock 높은 상태가 표시되며, 더 낮은 16 비트 기록 상태 (INT 타입 4 바이트, 총 32 비트)를 나타낸다.

 

비트 산술 연산은 각각 16 비트, 16 비트 고저 꺼내어 사용 가능.

모든 상태 막에 "0 (0 또는 1) 비트의 연산 결과가 0 (0 또는 1)이다있어서, 0 내지 16로 설정 높은 비트를 원하는 기록 상태의 하위 16 비트를 취하여 1 C & EXCLUSIVE_MASK ''는 동기화 exculusiveCount () 메소드 몸체에 구현 "및 EXCLUSIVE_MASK 동일 0x0000FFFF 자신의 조작의 결과 상태 0x0000FFFF"기능 비트 연산 식으로 사용될 수있다 "이다.

어느 높은 16 비트 부호없는 16 비트 우측 시프트, 동기화 sharedCount 메소드 본체 코드 "C >>> SHARED_SHIFT"을 갖고 국가는 단지 소거 상태의 하위 16 비트를, 상태를 판독한다.

        static final int SHARED_SHIFT   = 16;
        static final int SHARED_UNIT    = (1 << SHARED_SHIFT);
        static final int MAX_COUNT      = (1 << SHARED_SHIFT) - 1;
        static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;

        /** Returns the number of shared holds represented in count  */
        static int sharedCount(int c)    { return c >>> SHARED_SHIFT; }
        /** Returns the number of exclusive holds represented in count  */
        static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }

 

 5.写锁的获取与释放

 写锁的lock()方法实际调用Sync的父类AQS的acquire(int)方法,acquire(int)是模板方法,acquire(int)的主要逻辑在以前的帖子中分析过,我们重点关注被Sync重写的tryAcuqrie(int)方法,

    public void lock() {
        sync.acquire(1);
    }

    public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
                acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

 

 

tryAcquire的基本逻辑:如果读锁被获取了或写锁被其他线程获取了,那么尝试获取写锁失败。如果在之前当前线程已经获取了写锁,增加重入次数,尝试获取写锁成功。若未有任何读锁、写锁被获取,则进行CAS更新state,
若更新成功,尝试获取写锁成功,返回true,若更新失败,尝试获取写锁失败,返回false.
    protected final boolean tryAcquire(int acquires) {
        /*
         * Walkthrough:
         * 1. If read count nonzero or write count nonzero
         *    and owner is a different thread, fail.
         * 2. If count would saturate, fail. (This can only
         *    happen if count is already nonzero.)
         * 3. Otherwise, this thread is eligible for lock if
         *    it is either a reentrant acquire or
         *    queue policy allows it. If so, update state
         *    and set owner.
         */
        Thread current = Thread.currentThread();
        int c = getState();
        int w = exclusiveCount(c);//写锁重入的次数
        if (c != 0) {//当前锁至少持有1个读锁或1个写锁(任何情况下最多只能有一个写锁)
            // (Note: if c != 0 and w == 0 then shared count != 0)
            /**
             *  因为 共享锁重入次数+排他锁重入次数 < state ,即 shareCout+ w<c,而又c>0 && w=0,
             *  那么shareCount>0,当前读锁被某线程获取了,所以这里w=0表示当前读锁被某些线程获取了,
             *  在读锁被获取了的情况下,不能获取写锁(只有在所有读锁、写锁均补充释放才能获取写锁),返回false。
             *
             *  "current != getExclusiveOwnerThread()"为true表明,
             *   前置条件"w==0"不成立,那么w>0,即写锁已经被某线程获取到了,
             *   而"current != getExclusiveOwnerThread()"条件本身又表明,当前线程不是获取到写锁的线程
             *   所以尝试获取写锁失败,返回false
             *
             *   综合起来说,当有读锁被某些线程成功获取或写锁被其他线程成功获取时,
             *   尝试获取写锁失败,返回false。
             */
            if (w == 0 ||  current != getExclusiveOwnerThread())
                return false;
            if (w + exclusiveCount(acquires) > MAX_COUNT) //超出了最大可重入次数,MAX_COUNT=0x0000FFFF
                throw new Error("Maximum lock count exceeded");
            // Reentrant acquire
             //之前写锁已经被当前线程成功获取,重入次数自增
            //尝试获取写锁成功,返回true
            setState(c + acquires);
            return true;
        }
        
        //getState=0,当前锁不持有任何读锁、写锁
        
        if (writerShouldBlock() ||
                !compareAndSetState(c, c + acquires))
            return false; //cas更新失败,尝试获取写锁失败,返回false
        //cas更新成功,设置当前线程为独占线程,尝试获取写锁成功,,返回false
        setExclusiveOwnerThread(current);
        return true;
    }

 

 这里与ReentrantLock不同的地方在于多了对读锁是否存在的判断。读锁的读取线程进行读取操作时,它不能主动感知写入操作,如果同时进行读写操作,读入的数据可能是被删除的数据或是过期的数据,读取时不能写入,所以在已有读锁的时候不能再去获取写锁。反过来也是一样,为保证数据的一致性,在有写锁的时候,它要阻塞其他所有的读写操作,不能再去获取任何读锁和写锁。


tryRelease(int)尝试释放锁的基本逻辑和ReentrantLock几乎一样:将重入次数自减,当重入次数为0,将独占线程设为null,返回true,反之返回false.
        protected final boolean tryRelease(int releases) {
            if (!isHeldExclusively())
                throw new IllegalMonitorStateException();
            int nextc = getState() - releases;
            boolean free = exclusiveCount(nextc) == 0;
            if (free)
                setExclusiveOwnerThread(null);
            setState(nextc);
            return free;
        }

 

추천

출처www.cnblogs.com/gocode/p/12310430.html