>  기사  >  Java  >  ReadWriteLock 인터페이스 및 그 구현 ReentrantReadWriteLock

ReadWriteLock 인터페이스 및 그 구현 ReentrantReadWriteLock

巴扎黑
巴扎黑원래의
2017-06-23 16:33:391210검색

  기본적으로 Java 동시성 패키지의 잠금이 도입되었습니다. ReentrantLock이 핵심입니다. 동기화 장치 AQS의 작동 메커니즘을 명확하게 이해한 후에는 실제로 이러한 잠금을 분석하는 것이 훨씬 쉬울 것입니다. 이 장에서는 다른 것에 중점을 둡니다. 중요한 잠금-ReentrantReadWriteLock 읽기-쓰기 잠금입니다.

  ReentrantLock은 배타적 잠금입니다. 즉, 잠금은 하나의 스레드에서만 획득할 수 있습니다. 그러나 스레드가 읽기 작업만 수행하는 시나리오라면 어떨까요? 이런 방식으로 ReentrantLock은 그다지 적합하지 않습니다. 스레드의 보안을 보장할 필요는 없으며, 이러한 방식으로만 성능과 효율성을 최대한 보장할 수 있습니다. ReentrantReadWriteLock은 읽기 잠금과 쓰기 잠금으로 나누어지며, N개의 읽기 작업 스레드만 쓰기 잠금을 얻을 수 있을 것으로 예상됩니다. lock은 공유 잠금(AQS의 공유 모드)이고 읽기 잠금은 배타적 잠금(AQS의 배타적 모드)입니다. 먼저 읽기-쓰기 잠금의 인터페이스 클래스를 살펴보겠습니다.

1 public interface ReadWriteLock {    
2     Lock readLock();        //获取读锁3     Lock writeLock();        //获取写锁4 }

ReadWriteLock 인터페이스는 읽기 잠금을 획득하는 방법과 쓰기 잠금을 획득하는 방법이라는 두 가지 메서드만 정의하는 것을 볼 수 있습니다. 다음은 ReadWriteLock - ReentrantReadWriteLock 구현 클래스이다.

ReentrantLock과 마찬가지로 ReentrantReadWriteLock도 내부 클래스 Sync를 통해 동기화 장치 AQS를 구현하여 공정한 잠금과 불공정한 잠금을 구현합니다. ReadWriteLock 인터페이스에서 획득한 읽기 잠금 및 쓰기 잠금은 어떻게 구현됩니까?

//ReentrantReadWriteLockprivate final ReentrantReadWriteLock.ReadLock readerLock;private final ReentrantReadWriteLock.WriteLock writerLock;final Sync sync;public ReentrantReadWriteLock(){this(false);    //默认非公平锁}public ReentrantReadWriteLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();    //锁类型(公平/非公平)readerLock = new ReadLock(this);    //构造读锁writerLock = new WriteLock(this);    //构造写锁}
……public ReentrantReadWriteLock.WriteLock writeLock0{return writerLock;}public ReentrantReadWriteLock.ReadLock readLock0{return ReaderLock;}
//ReentrantReadWriteLock$ReadLockpublic static class ReadLock implements Lock {protected ReadLock(ReentrantReadwritLock lock) {
        sync = lock.sync;        //最后还是通过Sync内部类实现锁  }
    ……    //它实现的是Lock接口,其余的实现可以和ReentrantLock作对比,获取锁、释放锁等等}
//ReentrantReadWriteLock$WriteLockpublic static class WriteLock implemnts Lock {protected WriteLock(ReentrantReadWriteLock lock) {
        sync = lock.sync;
  }
……    //它实现的是Lock接口,其余的实现可以和ReentrantLock作对比,获取锁、释放锁等等}

위 내용은 ReentrantReadWriteLock에 대한 일반적인 소개입니다. 실제로 읽기-쓰기 잠금에는 ReadLock과 WriteLock이라는 두 개의 잠금이 있습니다. 두 잠금 모두 Self-Lock 인터페이스를 구현하며 ReentrantLock과 비교할 수 있습니다. 이 두 잠금의 내부 구현은 동기화 장치 AQS인 Sync를 통해 구현됩니다. 이는 ReentrantLock의 Sync와도 비교할 수 있습니다.
AQS를 되돌아보면 그 안에는 두 가지 중요한 데이터 구조가 있습니다. 하나는 동기화 대기열이고 다른 하나는 동기화 상태입니다. 이 동기화 상태는 읽기-쓰기 상태인 읽기-쓰기 잠금에 적용되지만 거기에 있습니다. AQS에서는 단 하나의 상태 정수입니다. 동기화 상태를 나타내기 위해 기록해야 하는 읽기-쓰기 잠금에 읽기 및 쓰기의 두 가지 동기화 상태가 있습니다. 따라서 읽기-쓰기 잠금은 AQS에서 상태 정수를 처리합니다. 이는 총 4바이트 및 32비트의 int 변수입니다. 그러면 읽기 및 쓰기 상태는 각각 16비트를 차지할 수 있습니다. 상위 16비트는 읽기를 나타냅니다. 16비트는 쓰기를 나타냅니다.

  

  이제 질문이 있습니다. 상태 값이 5이면 이진수는 (00000000000000000000000000000101)입니다. 이를 위해서는 변위 작업을 사용해야 합니다. 계산 방법은 쓰기 상태 상태 >>>16입니다. 쓰기 상태를 1 증가시키는 것은 상태 + 1과 같고, 읽기 상태를 1 증가시키는 것은 상태 +(1

쓰기 잠금 획득 및 해제

이전 경험을 바탕으로 AQS가 이미 잠금 획득을 위한 알고리즘 뼈대를 설정했으며 tryAcquire(독점 잠금)를 구현하려면 하위 클래스만 필요하다는 것을 알 수 있습니다. tryAcquire 를 확인하세요.

 1 //ReentrantReadWriteLock$Sync 2 protected final boolean tryAcquire(int acquires) { 3     Thread current = Thread.currentThread; 4     int c = getState();    //获取state状态 5     int w = exclusiveCount(c);    //获取写状态,即 state & 0x00001111 6     if (c != 0) {    //存在同步状态(读或写),作下一步判断 7         if (w == 0 || current != getExclusiveOwnerThread())     //写状态为0,但同步状态不为0表示有读状态,此时获取锁失败,或者当前已经有其他写线程获取了锁此时也获取锁失败 8             return false; 9         if (w + exclusiveCount(acquire) > MAX_COUNT)    //锁重入是否超过限制10             throw new Error(“Maxium lock count exceeded”);11         setState(c + acquire);    //记录锁状态12         return true;13   }14   if (writerShouldBlock() || !compareAndSetState(c, c + acquires))15       return false;        //writerShouldBlock对于非公平锁总是返回false,对于公平锁则判断同步队列中是否有前驱节点16   setExclusiveOwnerThread(current);17   return true;18 }

위는 쓰기 잠금의 상태 획득입니다. 이해하기 어려운 것은 WriterShouldBlock 메서드입니다. 이 메서드는 위에 설명되어 있지만 공정한 잠금의 경우 hasQueuedPredecessors 메서드가 다음과 같이 호출됩니다. :

1 //ReentrantReadWriteLock$FairSync2 final boolean writerShouldBlock() {3     return hasQueuedPredecessors();4 }

 이유는 무엇인가요? 이는 불공정 잠금과 공정 잠금의 차이점으로 돌아옵니다. 자세한 내용은 "5.Lock 인터페이스 및 구현 ReentrantLock"을 참조하세요. 불공정 잠금의 경우 스레드가 잠금을 획득할 때마다 동기화 대기열에 스레드가 있는지 여부에 관계없이 먼저 잠금 획득 작업을 강제로 수행합니다. 획득을 얻을 수 없으면 스레드는 대기열 끝까지 구성됩니다. 공정한 잠금의 경우 동기화 대기열이 있는 한 대기열에 스레드가 있으면 잠금이 획득되지 않지만 스레드 구조는 대기열 끝에 추가됩니다. 다시 쓰기 상태 획득으로 돌아가서 tryAcquire 메서드에서 잠금을 보유하는 스레드가 없음을 발견했지만 이때는 다른 잠금의 경우 해당 작업이 수행됩니다. - 잠금 잡기, 공정한 잠금의 경우. - 동기화 대기열 스레드에 스레드가 있고 잠금을 확보하지 않았으며 대기열 끝에 추가되었습니다.
쓰기 잠금 해제 프로세스는 기본적으로 ReentrantLock 해제 프로세스와 유사합니다. 결국 배타적 잠금입니다. 각 릴리스는 쓰기 잠금이 완전히 해제되었음을 의미하는 0으로 줄어들 때까지 쓰기 상태를 감소시킵니다.

읽기 잠금 획득 및 해제

동일한 방식으로 이전 경험을 바탕으로 AQS가 이미 잠금 획득을 위한 알고리즘 뼈대를 설정했으며 tryAcquireShared(공유 잠금)를 구현하려면 하위 클래스만 필요하다는 것을 알 수 있습니다. )이므로 tryAcquireShared만 확인하면 됩니다. 공유 모드의 잠금의 경우 여러 스레드가 동시에 획득할 수 있다는 것을 알고 있습니다. 이제 문제가 발생합니다. T1 스레드가 잠금을 획득하고 이때 동기화 상태는 상태=1입니다. lock, state=2, T1 스레드 재진입 상태=3, 즉 읽기 상태는 모든 스레드가 읽기 잠금을 획득한 횟수의 합이며, 각 스레드가 읽기 잠금을 획득한 횟수는 다음과 같습니다. ThreadLock에 저장되고 스레드 자체에 의해 유지 관리되므로 여기에서 몇 가지 작업을 수행해야 합니다. 처리가 복잡하고 소스 코드가 약간 길지만 각 스레드가 읽기 잠금을 획득한 횟수를 저장한다는 사실이 복잡합니다. 자세한 내용은 소스 코드의 tryAcquireShared를 참조하세요. 위의 쓰기 잠금 획득 분석과 결합하면 이해하기 어렵지 않습니다.
 읽기 잠금 해제에서 주목할만한 점은 자체적으로 유지되는 잠금 획득 횟수와 Shift 연산을 통한 상태 감소 – (1

위 내용은 ReadWriteLock 인터페이스 및 그 구현 ReentrantReadWriteLock의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.