Java 5.0 이전에는 공유 객체에 대한 액세스를 조정하는 데 사용할 수있는 유일한 메커니즘이 동기화되고 휘발성이었습니다. 동기화 된 키워드는 내장 잠금 장치를 구현하는 반면, 휘발성 키워드는 멀티 스레드의 메모리 가시성을 보장합니다. 대부분의 경우, 이러한 메커니즘은 작업을 잘 수행 할 수 있지만 잠금 장치를 획득하기 위해 대기 대기 스레드를 방해 할 수없고 시간 제한 잠금 장치를 구현할 수없고, 비 블로킹 구조에 대한 잠금 규칙을 구현할 수없고, 이러한 유연한 잠금 장치는 일반적으로 더 나은 활동이나 성능을 제공하는 것과 같은 고급 기능을 구현할 수는 없습니다. 따라서 Java 5.0 : ReintrantLock에는 새로운 메커니즘이 추가되었습니다. ReintrantLock 클래스는 잠금 인터페이스를 구현하고 동기화 된 것과 동일한 뮤텍스 및 메모리 가시성을 제공합니다. 기본 레이어는 AQ를 통해 멀티 스레드 동기화를 달성하는 것입니다. 내장 잠금 장치와 비교할 때, ReintrantLock은 더 풍부한 잠금 장치를 제공 할뿐만 아니라 성능의 내장 잠금 장치보다 열등하지는 않습니다 (이전 버전의 내장 잠금 장치보다 더 좋습니다). ReintrantLock의 많은 장점에 대해 이야기 한 결과 소스 코드를 밝히고 특정 구현을 보자.
1. 동기화 된 키워드 소개
Java는 다중 스레드 동기화를 지원하기 위해 내장 잠금 장치를 제공합니다. JVM은 동기화 된 키워드에 따라 동기화 된 코드 블록을 식별합니다. 스레드가 동기화 된 코드 블록에 들어가면 자동으로 잠금을 획득합니다. 동기화 된 코드 블록을 종료하면 잠금이 자동으로 해제됩니다. 한 스레드가 잠금을 획득하면 다른 스레드가 차단됩니다. 각 Java 객체는 동기화를 구현하는 잠금으로 사용할 수 있습니다. 동기화 된 키워드를 사용하여 객체 메소드, 정적 메소드 및 코드 블록을 수정할 수 있습니다. 객체 방법과 정적 메소드를 수정할 때 잠금은 메소드가 위치한 객체와 클래스 객체입니다. 코드 블록을 수정할 때는 추가 객체를 잠금으로 제공해야합니다. 각 Java 객체를 잠금으로 사용할 수있는 이유는 모니터 객체 (조작)가 객체 헤더에 연결되기 때문입니다. 스레드가 동기 코드 블록으로 들어가면 모니터 객체를 자동으로 고정하고 종료되면 자동으로 모니터 객체를 해제합니다. 모니터 객체가 유지되면 다른 스레드가 차단됩니다. 물론 이러한 동기화 작업은 JVM 기본 계층에 의해 구현되지만 동기화 된 키워드 수정 방법 및 코드 블록의 기본 구현에는 여전히 약간의 차이가 있습니다. 동기화 된 키워드 수정 방법은 암시 적으로 동기화됩니다. 즉, 바이트 코드 지침을 통해 제어 할 필요가 없습니다. JVM은 메소드가 메소드 테이블의 ACC_SynChronized Access 플래그를 기반으로 동기화 된 메소드인지 여부를 구별 할 수 있습니다. 동기화 된 키워드로 수정 된 코드 블록은 명시 적으로 동기화되며, 이는 Moniterenter 및 MonitorExit Bytecode 지침을 통해 파이프 라인의 보유 및 해제를 제어합니다. 모니터 객체는 내부적으로 _count 필드를 보유합니다. _count는 0과 동일하다는 것은 파이프 라인이 유지되지 않았 음을 의미하며 _count는 0보다 큰 파이프 라인이 유지되었음을 의미합니다. 홀딩 스레드 재창조가있을 때마다 _count가 추가 될 때마다 1이 추가되고 홀딩 스레드가 종료 될 때마다 _count는 1 씩 줄어 듭니다. 이는 내장 잠금 재진입의 구현 원리입니다. 또한 모니터 객체 _entryList 및 _waitset에는 동기화 큐 및 조건부 AQS에 해당하는 두 개의 큐가 있습니다. 스레드가 잠금을 얻지 못하면 _entrylist에서 차단됩니다. 잠금 객체의 대기 방법이 호출되면 스레드가 _waitset로 들어가서 대기합니다. 이것은 스레드 동기화의 구현 원리입니다.
2. 재진입 락과 동기화 된 비교
동기화 된 키워드는 Java가 제공하는 내장 잠금 장치입니다. 동기화 작업은 기본 JVM에 의해 구현됩니다. ReintrantLock은 java.util.concurrent 패키지가 제공하는 명시 적 잠금 장치이며 동기화 작업은 AQS 동기화기로 구동됩니다. ReintrantLock은 내장 잠금 장치와 잠금 및 메모리에 대한 동일한 의미를 제공 할뿐만 아니라 타임 메드 잠금 대기, 인터럽트 잠금 대기 대기, 공정한 잠금 및 비 블록 구조적 잠금 구현 등의 다른 기능을 제공합니다. 또한 ReintrantLock은 초기 JDK 버전에서 특정 성능 이점이있었습니다. ReintrantLock에 많은 장점이 있으므로 동기화 된 키워드를 사용해야하는 이유는 무엇입니까? 실제로, 많은 사람들이 ReintrantLock을 사용하여 동기화 된 키워드의 잠금 작동을 대체합니다. 그러나 내장 잠금 장치에는 여전히 고유 한 장점이 있습니다. 내장 잠금 장치는 많은 개발자에게 친숙하며 더 간단하고 컴팩트합니다. 명시 적 잠금 장치는 마지막으로 잠금 해제로 호출되어야하므로 내장 잠금 장치를 사용하는 것이 비교적 안전합니다. 동시에, 앞으로 재진입 락보다는 동기화의 성능을 향상시킬 가능성이 더 높습니다. Synchronized는 JVM의 내장 속성이기 때문에 스레드에 밀착 된 잠금 객체에 대한 잠금 제거 최적화와 같은 일부 최적화를 수행 할 수 있으며, 잠금의 세분성을 증가시켜 내장 잠금 장치의 동기화를 제거하고 클래스 라이브러리 기반 잠금을 통해 구현되면 가능성이 높습니다. 따라서 일부 고급 기능이 필요한 경우, 적절한, 투표 가능하며 인터럽트 가능한 잠금 획득 작업, 공정한 대기열 및 비 블록 구조 잠금 장치를 포함하여 ReintrantLock을 사용해야합니다. 그렇지 않으면 동기화 된 것이 먼저 사용해야합니다.
3. 자물쇠 획득 및 방출 작업
먼저 ReentrantLock을 사용하여 잠금 장치를 추가하여 샘플 코드를 살펴 보겠습니다.
public void dosomething () {// 기본값은 비 공석 잠금 재 렌트 런트 락 잠금을 얻는 것입니다. 실행하기 전에 {// lock.lock ()을 시도하십시오. // 작업을 실행합니다 ...} 마침내 {// 잠금 잠금 장치 .unlock ()가 최종적으로 릴리스됩니다. }}다음은 자물쇠를 획득하고 공개하기위한 API입니다.
// 잠금을 얻는 작업 공개 void lock () {sync.lock ();} // 잠금 잠금 해제 작업 공개 void unlock () {sync.release (1);}잠금을 획득하고 잠금을 릴리스하는 작업이 각각 동기화 객체의 잠금 방법 및 릴리스 방법에 위임된다는 것을 알 수 있습니다.
공개 클래스 ReintrantLock은 잠금, Java.io.serializable {Private Final Sync Sync; 초록 정적 클래스 동기화 확장 acpractQueuedSynchronizer {Abstract void lek (); } // 비-아프 잠금 정적 최종 클래스를 구현하는 동기화기는 동기화 {final void lock () {...}} // synchronizer가 페어 잠금 정적 최종 클래스 fairsync intends sync {final void lock () {...}}}을 구현합니다.각 재진입 락 오브젝트는 유형 동기화를 참조합니다. 이 동기화 클래스는 추상 내부 클래스입니다. AbstractQueuedSynchronizer에서 상속합니다. 내부의 잠금 방법은 추상 방법입니다. ReintrantLock의 멤버 변수 동기화는 시공 중에 값이 할당됩니다. 재진입 락의 두 가지 생성자 방법이 무엇을하는지 살펴 보겠습니다.
// 기본 매개 변수가없는 생성자 public reintrantlock () {sync = new nonfairsync ();} // 매개 변수화 된 생성자 public reintrantlock (부울 페어) {sync = fair? New FairSync () : New NonFairsync ();}기본 매개 변수가없는 생성자를 호출하면 동기화되지 않은 인스턴스가 동기화되도록 할당하며 잠금은 현재 비 공석 잠금 장치입니다. 매개 변수 생성기를 사용하면 파라미터가 동기화 할 FairSync 인스턴스 또는 비 페어 동기 인스턴스를 할당할지 여부를 지정할 수 있습니다. Nonfairsync 및 FairSync는 SYNC 클래스에서 상속하고 Lock () 메소드를 다시 작성하므로 Fair Locks와 Fair Locks간에 잠금 장치를 얻는 방식에 약간의 차이가 있습니다. 자물쇠를 릴리스하는 작업을 살펴 보겠습니다. Unlock () 메소드를 호출 할 때마다 Sync.Release (1) 작동을 실행합니다. 이 작업은 AbstractQueuedSynchronizer 클래스의 release () 메소드를 호출합니다. 다시 검토합시다.
// 잠금 작동 (독점 모드) 공개 최종 부울 릴리스 (int arg) {// 비밀번호 잠금을 돌려서 잠금을 해제 할 수 있는지 확인하십시오. // 헤드 노드가 비어 있지 않고 대기 상태가 0과 같지 않으면 (h! = null && h.waitstatus! = 0) if if if if if If If If If If If If If If If If inc } true를 반환합니다. } return false;}이 릴리스 방법은 AQS가 제공하는 잠금 작업을 해제하기위한 API입니다. 먼저 tryrelease 메소드를 호출하여 잠금 장치를 얻으려고합니다. TryRelease 메소드는 추상 방법이며 구현 로직은 서브 클래스 동기화에 있습니다.
// 잠금 보호 된 최종 부울 트리 릴 데스 (int 릴리스) {int c = getState () - 릴리스를 해제하십시오. // 잠금을 고정하는 스레드가 현재 스레드가 아닌 경우 (thread.currentThread ()! = getExclusiveOwnerThread ()) {throw new 불법 모니터 스테이트 exception (); } 부울 free = false; // 동기화 상태가 0이면 (c == 0)이면 잠금이 릴리스되었음을 의미합니다. {// 잠금 장치의 플래그를 true free = true로 설정합니다. // 점유 된 스레드를 빈 setExcluctionownerthread (null)로 설정합니다. } setState (c); 무료 반품;}이 TryRelease 방법은 먼저 전류 동기화 상태를 획득하고, 전달 된 매개 변수에서 새 동기화 상태로의 전류 동기화 상태를 빼고, 새 동기화 상태가 0과 동일하는지 여부를 결정합니다. 0과 같으면 현재 잠금이 해제되었음을 의미합니다. 그런 다음 잠금의 릴리스 상태를 True로 설정 한 다음 현재 잠금을 차지하는 스레드를 지우고 마지막으로 SetState 메서드를 호출하여 새 동기화 상태를 설정하고 잠금 상태를 반환합니다.
4. 공정한 자물쇠와 불공정 한 자물쇠
우리는 어떤 특정 인스턴스가 동기화를 기반으로 지적하는 재진입 락이 어떤 인스턴스인지 알고 있습니다. 구성 중에 멤버 변수 동기화가 할당됩니다. 값이 비 Fairsync 인스턴스에 할당 된 경우, 비 공석 잠금 장치이고 값이 FairSync 인스턴스에 할당되면 공정한 잠금임을 의미합니다. 공정한 잠금 장치 인 경우, 스레드는 요청을하는 순서대로 잠금을 얻지 만 불공평 한 잠금 장치에서는 절단 동작이 허용됩니다. 스레드가 불공정 한 잠금을 요청하면 요청이 발행되는 것과 동시에 잠금 상태를 사용할 수있게되면 스레드는 queue의 모든 대기 스레드를 건너 뛸 수 있습니다. 불공정 한 자물쇠를 얻는 방법을 살펴 보겠습니다.
// 불공정 동기화 정적 최종 클래스 정적 최종 클래스 unfairsync 확장 동기화 {// 부모 클래스의 추상 메소드 구현 잠금 Final void lock () {// cas 메서드를 사용하여 동기화 상태를 설정하여 (0, 1)를 설정하면 설정이 성공적이면 SetExclusionTherthhThread (thread.currentThrede)를 점령하지 않음을 의미합니다. } else {// 그렇지 않으면 잠금이 점유되었음을 의미합니다. 호출 획득 및 스레드 큐가 큐를 동기화하여 획득 (1); }} // 잠금 보호 된 최종 부울 트리 아카 Quire (int arcides) {return nonfairtryacquire (획득); }} // 무기 모드에서 잠금을 취득 (독점 모드) 공개 최종 무효 획득 (int arg) {if (! tryacquire (arg) && acquirequeued (addwaiter (node.exclusive)) {secolinterrupt (); }}불공정 잠금의 잠금 방법에서 스레드는 CAS의 첫 번째 단계에서 동기화 상태의 값을 0에서 1으로 변경한다는 것을 알 수 있습니다. 실제로,이 작업은 잠금을 획득하려고 시도하는 것과 같습니다. 변경이 성공하면 스레드가 지금 잠금을 획득했으며 더 이상 동기화 큐에서 대기열이 필요하지 않음을 의미합니다. 변경 사항이 실패하면 스레드가 처음 오면 잠금이 해제되지 않았으므로 획득 방법이 다음에 호출됩니다. 우리는이 획득 방법이 AbstractQueuedSynchronizer 메소드에서 상속된다는 것을 알고 있습니다. 이 방법을 검토합시다. 스레드가 획득 메소드에 들어가면, 첫 번째 호출 The TryAcquire 메소드를 호출하여 잠금을 얻으려고합니다. Nonfairsync는 TheyAcquire 메소드를 덮어 쓰고 메소드에서 부모 클래스 동기화의 비 이어 트리 콰이어 메소드를 호출하기 때문에 비고 항공기 Quire 메소드는 여기에서 호출되어 잠금을 얻습니다. 이 방법이 구체적으로 무엇을하는지 봅시다.
// 잠금의 불공정 한 최종 획득 최종 부울 nonfairtryAcquire (int arcides) {// 현재 스레드 최종 스레드를 가져옵니다. // 현재 동기화 상태를 가져옵니다. int c = getState (); // 동기화 상태가 0 인 경우 잠금이 (c == 0)이라도 점유되지 않음을 의미합니다. (c == 0) {// cas를 사용하여 동기화 상태를 업데이트하여 (compareAndsetState (0, 획득)) {// 현재 잠금 SetExclusiveNerthread (current)를 차지하고있는 스레드를 설정합니다. 진실을 반환하십시오. } // 그렇지 않으면, 잠금이 현재 스레드인지 여부} else if (current == getExcluctionOwnerThread ()) {// 잠금이 현재 스레드에 의해 유지되면 현재 동기화 상태를 직접 수정하면 int nextc = c + 획득; if (nextc <0) {Throw New Error ( "최대 잠금 수를 초과"); } setstate (nextc); 진실을 반환하십시오. } // 잠금이 현재 스레드가 아닌 경우 실패 플래그를 반환합니다.비고어 트리 키이어 방법은 동기화 메소드입니다. 스레드 가이 메소드에 들어간 후 먼저 동기화 상태를 얻는다는 것을 알 수 있습니다. 동기화 상태가 0 인 경우 CAS 작업을 사용하여 동기화 상태를 변경하십시오. 사실, 이것은 잠금을 다시 얻는 것입니다. 동기화 상태가 0이 아닌 경우 잠금이 점유되었음을 의미합니다. 현재 잠금을 고정하는 스레드가 현재 스레드인지 먼저 결정합니다. 그렇다면 동기화 상태가 1만큼 증가합니다. 그렇지 않으면 잠금을 획득하려는 작업이 실패합니다. 따라서 addwaiter 메소드가 호출되어 스레드를 동기화 큐에 추가합니다. 요약하면, 불공정 잠금 모드에서 스레드는 동기화 큐를 입력하기 전에 두 개의 잠금 장치를 얻으려고합니다. 획득이 성공하면 동기화 큐 큐 큐 큐 큐가 들어 가지 않으면 동기화 큐 큐 큐 큐가 들어갑니다. 다음으로 공정한 자물쇠를 얻는 방법을 살펴 보겠습니다.
// 페어 잠금 정적 최종 클래스 fairsync 확장 동기화 {// 부모 클래스의 추상 메소드 구현 획득 최종 void lock () {// 획득 획득을 획득하여 큐를 동기화하기 위해 스레드 큐가 획득을 얻습니다 (1); } // 잠금을 얻으려고 노력하십시오. 보호 된 최종 Boolean tryacquire (int arciers) {// 현재 스레드 최종 스레드를 가져옵니다. // 현재 동기화 상태를 가져옵니다. int c = getState (); // 동기화 상태 0이 잠금이 점유되지 않음을 의미하는 경우 (c == 0) {// 동기화 큐에 전방 노드가 있는지 (! hasqueuedPredecessors () && compraleAndsetstate (0, 획득)) {// 동기화 상태가없고 성공이 성공적으로 획득된다는 것을 의미합니다. setExcluboryownerthread (current); 진실을 반환하십시오. } // 그렇지 않으면, 현재 스레드가 잠금을 고정하는지 여부를 결정하십시오. if (nextc <0) {Throw New Error ( "최대 잠금 수를 초과"); } setstate (nextc); 진실을 반환하십시오. } // 현재 스레드가 잠금을 유지하지 않으면 획득이 False를 반환합니다. }} Fair Lock의 잠금 방법을 호출 할 때 획득 방법이 직접 호출됩니다. 마찬가지로, 획득 방법은 먼저 FairSync Rewrite TryAcquire 메소드를 호출하여 잠금을 얻으려고 시도합니다. 이 방법에서, 동기화 상태의 값이 먼저 얻어진다. 동기화 상태가 0이면 현재 잠금이 해제되었음을 의미합니다. 불공정 한 잠금의 차이점은 먼저 HasqueuedPredecessors 메소드를 호출하여 누군가가 동기화 큐에서 대기 중인지 확인한다는 것입니다. 아무도 대기하지 않으면 동기화 상태의 값이 수정됩니다. Fair Lock이 즉시 잠금을 획득하는 대신 예의있는 방법을 채택한다는 것을 알 수 있습니다. 불공정 한 자물쇠와 다른이 단계를 제외하고 다른 작업은 동일합니다. 요약하면, 우리는 동기화 큐를 입력하기 전에 Fair Lock이 잠금 상태 만 한 번만 점검한다는 것을 알 수 있습니다. 잠금 장치가 열려 있음을 발견하더라도 즉시 획득하지 않습니다. 대신, 동기화 큐의 스레드가 먼저 얻을 수 있도록합니다. 따라서 모든 스레드가 박람회 잠금 장치에서 자물쇠를 획득하는 순서가 먼저 도착하여 도착하여 자물쇠를 얻는 공정성을 보장 할 수 있습니다.
그렇다면 왜 우리는 모든 자물쇠가 공정하기를 원하지 않습니까? 결국, 공정성은 좋은 행동이며 불공평성은 나쁜 행동입니다. 스레드의 일시 중단 및 모닝 작업은 큰 오버 헤드를 가지기 때문에 시스템 성능에 영향을 미치기 때문에 특히 치열한 경쟁의 경우 공정한 자물쇠는 스레드의 빈번한 일시 중단 및 웨이크 업 운영으로 이어질 것이며, 비전제 잠금 장치는 이러한 작업을 줄일 수 있으므로 성능의 공정한 자물쇠보다 더 좋습니다. 또한 대부분의 스레드는 매우 짧은 시간 동안 잠금 장치를 사용하고 스레드의 모닝 작업이 지연되기 때문에 스레드 B가 잠금을 즉시 획득하고 사용 후 잠금을 해제 할 수 있습니다. 이것은 상생 상황으로 이어집니다. 스레드 A가 잠금을 취득하는 순간은 지연되지 않지만 스레드 B는 잠금을 미리 사용하고 처리량도 개선되었습니다.
5. 조건부 대기열의 구현 메커니즘
내장 조건 큐에 약간의 결함이 있습니다. 각 내장 잠금 잠금 장치에는 하나의 관련 조건 큐 만 가질 수 있으므로 여러 스레드가 동일한 조건 큐에서 다른 조건에 대해 대기 할 수 있습니다. 그런 다음 NotifyAll이 호출 될 때마다 모든 대기 스레드가 깨어납니다. 실이 깨어날 때, 그것은 그것이 기다리고있는 조건 술어가 아니라는 것을 알게되며, 그것이 중단 될 것입니다. 이로 인해 많은 쓸모없는 스레드 웨이크 업 및 중단 작업으로 이어지면 많은 시스템 리소스를 낭비하고 시스템 성능을 줄입니다. 여러 조건부 곤경에 동시 객체를 쓰려고하거나 조건부 대기열 가시성보다 더 많은 제어를 얻으려면 내장 잠금 및 조건부 대기열 대신 명시 적 잠금 및 조건을 사용해야합니다. 조건과 잠금 장치는 조건 큐 및 내장 잠금 장치와 마찬가지로 함께 연결됩니다. 조건을 만들려면 관련 잠금에서 잠금 장치를 호출 할 수 있습니다. 먼저 조건을 사용하여 예제를 살펴 보겠습니다.
Public Class BoundedBuffer {Final Lock Lock = New ReintrantLock (); 최종 조건 notefull = lock.newcondition (); // 조건 술어 : Notefull 최종 조건 notempty = lock.newcondition (); // 조건 술어 : NOTEMPTY FINAL OBJECT [] 항목 = 새 개체 [100]; int putptr, takeptr, count; // 생산 방법 public void put (Object x)가 중단 된 결과 {lock.lock (); try {while (count == items.length) notfull.await (); // 대기열이 가득 차고 스레드가 무의미한 큐에서 [풋 프트] 항목을 기다리고 있습니다. 항목 [putptr] = x; if (++ putptr == items.length) putptr = 0; ++ 수; notempty.signal (); // 생산이 성공적이어서, NOTEMPTY 대기열의 노드를 깨우십시오} 마침내 {lock.unlock (); }} // 소비 방법 public object take ()가 interruptedException {lock.lock (); try {while (count == 0) notempty.await (); // 큐는 비어 있고, 스레드는 객체 x = airet [takeptr]를 대기합니다. if (++ takeptr == items.length) takeptr = 0; --세다; notfull.signal (); // 소비가 성공적 이어, 무의미한 큐 return x의 노드를 깨우십시오. } 마침내 {lock.unlock (); }}}잠금 객체는 여러 조건 대기열을 생성 할 수 있으며 두 개의 조건 대기열이 여기에서 두 가지 조건이 생성됩니다. 컨테이너가 가득 차면 풋 방법을 호출하는 스레드를 차단해야합니다. 조건 술어가 참을 때까지 기다리십시오 (컨테이너가 만족되지 않음)가 깨어나 계속 실행됩니다. 컨테이너가 비어 있으면 테이크 메소드를 호출하는 스레드를 차단해야합니다. 조건 술어가 사실이 될 때까지 기다리십시오 (컨테이너가 비어 있지 않음)가 깨어나 계속 실행됩니다. 이 두 가지 유형의 스레드는 다른 조건에 따라 대기하므로, 두 개의 다른 조건 대기열을 차단하기 위해 입력하고 조건 객체에서 API를 호출하여 깨어나 기 전에 올바른 시간까지 기다립니다. 다음은 NewCondition 방법의 구현 코드입니다.
// 조건 대기열 생성 공개 조건 newCondition () {return sync.newcondition ();} 초록 정적 클래스 동기화 extract QuectureUedSynCronizer {// 새 조건 개체 생성 최종 조건 Object newCondition () {return new ConditionObject (); }}ReintrantLock에서 조건 큐의 구현은 AbstractQueuedSynchronizer를 기반으로합니다. NewCondition 메소드를 호출 할 때 얻을 수있는 조건 객체는 AQ의 내부 클래스 조건 관습의 인스턴스입니다. 조건 큐의 모든 작업은 ConditionObject에서 제공 한 API를 호출하여 수행됩니다. ConditionObject의 특정 구현을 위해서는 "Java Concurrency Series [4] ----- AbstractQueuedSynchronizer 소스 코드 분석 조건 큐"를 확인할 수 있으며 여기에서 반복하지 않을 것입니다. 이 시점에서 재진입 락의 소스 코드에 대한 분석이 끝났습니다. 이 기사를 읽는 것이 독자들이 ReentrantLock을 이해하고 마스터하는 데 도움이되기를 바랍니다.
위는이 기사의 모든 내용입니다. 모든 사람의 학습에 도움이되기를 바랍니다. 모든 사람이 wulin.com을 더 지원하기를 바랍니다.