다음 편집기는 ReadWriteLock 인터페이스와 그 구현인 ReentrantReadWriteLock 메서드에 대한 기사를 제공합니다. 편집자님이 꽤 좋다고 생각하셔서 지금 공유하고 모두에게 참고용으로 드리고자 합니다. 편집기를 따라가서 살펴보겠습니다.
Java 동시성 패키지의 잠금은 기본적으로 ReentrantLock이 핵심입니다. 동기화 장치 AQS의 작동 메커니즘을 명확하게 이해하면 실제로 훨씬 더 쉬울 것입니다. 이러한 잠금을 분석합니다. 이 장에서는 또 다른 중요한 잠금인 ReentrantReadWriteLock 읽기-쓰기 잠금에 중점을 둡니다.
ReentrantLock은 배타적 잠금입니다. 즉, 잠금은 하나의 스레드에서만 획득할 수 있습니다. 그러나 스레드가 읽기 작업만 수행하는 시나리오라면 어떻게 될까요? 이런 방식으로 ReentrantLock은 그다지 적합하지 않습니다. 스레드의 보안을 보장할 필요는 없으며, 이러한 방식으로만 성능과 효율성을 최대한 보장할 수 있습니다. ReentrantReadWriteLock은 읽기 잠금과 쓰기 잠금으로 나누어지며, N개의 읽기 작업 스레드만 쓰기 잠금을 얻을 수 있을 것으로 예상됩니다. lock은 공유 잠금(AQS의 공유 모드)이고 읽기 잠금은 배타적 잠금(AQS의 배타적 모드)입니다. 먼저 읽기-쓰기 잠금의 인터페이스 클래스를 살펴보겠습니다. public interface ReadWriteLock {
Lock readLock(); //获取读锁
Lock writeLock(); //获取写锁
}
ReadWriteLock 인터페이스는 읽기 잠금을 획득하는 방법과 쓰기 잠금을 획득하는 방법이라는 두 가지 방법만 정의하는 것을 볼 수 있습니다. 다음은 ReadWriteLock - ReentrantReadWriteLock 구현 클래스이다.
ReentrantLock과 유사하게 ReentrantReadWriteLock도 내부 클래스 Sync를 통해 동기화 장치 AQS를 구현합니다. 또한 Sync를 구현하여 공정한 잠금과 불공정한 잠금을 구현합니다. 이 점의 아이디어는 ReentrantLock과 유사합니다. ReadWriteLock 인터페이스에서 획득한 읽기 잠금 및 쓰기 잠금은 어떻게 구현됩니까?
//ReentrantReadWriteLock private 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$ReadLock public static class ReadLock implements Lock { protected ReadLock(ReentrantReadwritLock lock) { sync = lock.sync; //最后还是通过Sync内部类实现锁 } …… //它实现的是Lock接口,其余的实现可以和ReentrantLock作对比,获取锁、释放锁等等 }
//ReentrantReadWriteLock$WriteLock public 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에는 동기화 상태를 나타내는 A 상태 정수만 있습니다. 읽기-쓰기 잠금에는 기록해야 하는 읽기 및 쓰기의 두 가지 동기화 상태가 있습니다. 따라서 읽기-쓰기 잠금은 AQS에서 상태 정수를 처리합니다. 이는 총 4바이트 및 32비트의 int 변수입니다. 그러면 읽기 및 쓰기 상태는 각각 16비트를 차지할 수 있습니다. 상위 16비트는 읽기를 나타냅니다. 16비트는 쓰기를 나타냅니다.
질문이 있습니다. 상태 값이 5이면 이진수는 (00000000000000000000000000000101)입니다. 이를 위해서는 변위 작업을 사용해야 합니다. 계산 방법은 쓰기 상태 상태 >>>16입니다. 쓰기 상태를 1 증가시키는 것은 상태 + 1과 같고, 읽기 상태를 1 증가시키는 것은 상태 +(1 에 대해서는 "
071af19a55f4da1989e8c02b755ba052>, >>> 시프트 연산"을 참조하세요. 쓰기 잠금 획득 및 해제
이전 경험에 따르면 AQS는 이미 잠금 획득을 위한 알고리즘 뼈대를 설정했으며 tryAcquire(독점 잠금)를 구현하려면 하위 클래스만 필요하다는 것을 알 수 있습니다. tryAcquire 를 확인해야 합니다.
//ReentrantReadWriteLock$Sync protected final boolean tryAcquire(int acquires) { Thread current = Thread.currentThread; int c = getState(); //获取state状态 int w = exclusiveCount(c); //获取写状态,即 state & 0x00001111 if (c != 0) { //存在同步状态(读或写),作下一步判断 if (w == 0 || current != getExclusiveOwnerThread()) //写状态为0,但同步状态不为0表示有读状态,此时获取锁失败,或者当前已经有其他写线程获取了锁此时也获取锁失败 return false; if (w + exclusiveCount(acquire) > MAX_COUNT) //锁重入是否超过限制 throw new Error(“Maxium lock count exceeded”); setState(c + acquire); //记录锁状态 return true; } if (writerShouldBlock() || !compareAndSetState(c, c + acquires)) return false; //writerShouldBlock对于非公平锁总是返回false,对于公平锁则判断同步队列中是否有前驱节点 setExclusiveOwnerThread(current); return true; }위는 쓰기 잠금의 상태 획득입니다. 이해하기 어려운 것은 WriterShouldBlock 메서드입니다. 이 메서드는 위에 설명되어 있지만 공정한 잠금의 경우 hasQueuedPredecessors 메서드가 다음과 같이 호출됩니다.
//ReentrantReadWriteLock$FairSync final boolean writerShouldBlock() { return hasQueuedPredecessors(); }
이유는 왜? 이는 불공정 잠금과 공정 잠금의 차이점으로 돌아옵니다. 자세한 내용은 "5.Lock 인터페이스 및 구현 ReentrantLock"을 참조하세요. 불공정한 잠금의 경우 스레드가 잠금을 획득할 때마다 동기화 대기열에 스레드가 있는지 여부에 관계없이 먼저 강제로 잠금 획득 작업을 수행합니다. 획득을 얻을 수 없는 경우 스레드는 대기열의 끝까지 구성됩니다. ; 동기화 큐가 있는 동안 공정한 잠금을 위해 큐에 스레드가 있는 경우 잠금은 획득되지 않지만 스레드 구조는 큐 끝에 추가됩니다. 다시 쓰기 상태 획득으로 돌아가서 tryAcquire 메서드에서 잠금을 보유하는 스레드가 없음을 발견했지만 이때는 다른 잠금의 경우 해당 작업이 수행됩니다. - 잠금 잡기, 공정한 잠금의 경우. - 동기화 대기열 스레드에 스레드가 있고 잠금을 확보하지 않았으며 대기열 끝에 추가되었습니다.
쓰기 잠금 해제 프로세스는 기본적으로 ReentrantLock의 해제 프로세스와 유사합니다. 결국 모두 배타적 잠금입니다. 각 릴리스는 쓰기 잠금이 완전히 해제될 때까지 쓰기 상태를 감소시킵니다.
읽기 잠금 획득 및 해제마찬가지로 이전 경험을 바탕으로 AQS가 잠금 획득을 위한 알고리즘 골격을 이미 설정했으며 tryAcquireShared(공유 잠금)를 구현하는 데 하위 클래스만 필요하므로 tryAcquireShared만 확인하면 된다는 것을 알 수 있습니다. 공유 모드의 잠금의 경우 여러 스레드가 동시에 획득할 수 있다는 것을 알고 있습니다. 이제 문제가 발생합니다. T1 스레드가 잠금을 획득하고 이때 동기화 상태는 상태=1입니다. lock, state=2, T1 스레드 재진입 상태=3, 즉 읽기 상태는 모든 스레드가 읽기 잠금을 획득한 횟수의 합이며, 각 스레드가 읽기 잠금을 획득한 횟수는 다음과 같습니다. ThreadLock에 저장되고 스레드 자체에 의해 유지 관리되므로 여기에서 몇 가지 작업을 수행해야 합니다. 처리가 복잡하고 소스 코드가 약간 길지만 각 스레드가 읽기 잠금을 획득한 횟수를 저장한다는 사실이 복잡합니다. 자세한 내용은 소스 코드의 tryAcquireShared를 참조하세요. 위의 쓰기 잠금 획득 분석과 결합하면 이해하기 어렵지 않습니다.
읽기 잠금 해제에서 주목할만한 점은 잠금 획득을 유지하는 횟수와 Shift 연산을 통한 상태 감소-(1
위 내용은 JAVA의 ReadWriteLock 인터페이스 및 그 구현 ReentrantReadWriteLock 메소드의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!