자바 동시 프로그래밍 요약 6 : 잠금 프레임 워크, AQS 상세

학교 모집 인터뷰를 준비하고 있기 때문에이 지식은 더 이상 초보자가 아니며 초보자로 구성하기에는 너무 느립니다.

이전에 작성한 블로그를 직접 살펴보기로 결정한 여기에는 구조 형성을 용이하게하기위한 지식 조합의 프레임 워크 만 있습니다.

누락 된 지식 포인트가 있으면 자세한 보충 자료를 작성하십시오.

 

자바 동시 프로그래밍 (5) : Lock, AbstractQueuedSynchronizer에 대한 자세한 설명

1. 잠금 정보 :

0, 동시 패키지 아키텍처 :

1. 잠금 패키지 아키텍처 및 소개 :

2. 잠금과 동기화의 간단한 비교

  • 잠금은 인터페이스, 코드 레벨의 구현, 동기화는 키워드, 내장 언어 구현 (JVM 레벨)입니다.
  • 잠금 해제를 획득하기 위해 잠금이 표시 되고, 확장 성이 높고, 동기화가 암묵적 으로 잠금 해제되고, 더 간단합니다.
  • Lock에서 예외가 발생하면 unlock ()을 통해 잠금이 해제되지 않으면 교착 상태가 발생할 가능성이 있으므로 Lock 사용시 finally 블록에서 잠금을 해제해야합니다. 예외 발생시, 동기화는 스레드가 보유한 잠금자동으로 해제하므로 교착 상태가 발생하지 않습니다.
  • 잠금 잠금 이 인터럽트에 응답하기를 기다리는 스레드를 허용하고 동기화를 사용할 때 잠금 을 기다리는 스레드 는 영원히 대기하고 인터럽트에 응답 할 수 없습니다 .
  • 잠금은 비 차단, 인터럽트 가능 및 제한 시간 잠금을 획득하려고 시도 할 수 있지만 동기화는 불가능합니다.
  • 잠금은 성공적으로 잠금을 획득했는지 알 수 있지만 동기화는 알 수 없습니다.

3. 잠금 구조의 일반적인 방법 : lock (), unlock (), lockInterruptibly (), tryLock ()

4. ReadWriteLock에 대해 간단히 언급합니다.

ReadWriteLock 인터페이스는 읽기 잠금 및 쓰기 잠금을 획득하기위한 메서드를 제공 하는 별도의 인터페이스 (Lock 인터페이스에서 상속되지 않음) 입니다.

소위 읽기-쓰기 잠금은 관련 잠금-읽기 잠금 및 쓰기 잠금의 쌍입니다. 읽기 잠금은 읽기 전용 작업에 사용되고 쓰기 잠금은 쓰기 작업에 사용됩니다. 읽기 잠금은 동시에 여러 스레드에서 보유 할 수 있지만 쓰기 잠금은 배타적이며 하나의 스레드에서만 획득 할 수 있습니다.

앞서 언급 한 잠금 (Lock 인터페이스 및 동기화 된 키워드 포함)은 모두 배타적 잠금이며 이러한 잠금은 동시에 하나의 스레드 만 액세스 할 수 있도록합니다. 읽기-쓰기 잠금을 사용하면 여러 판독기 스레드가 동시에 액세스 할 수 있지만 작성기 스레드가 액세스하면 모든 판독기 스레드 및 기타 작성기 스레드가 차단됩니다. 읽기-쓰기 잠금은 한 쌍의 잠금, 읽기 잠금 및 쓰기 잠금을 유지하며 읽기 잠금과 쓰기 잠금을 분리하여 일반 독점 잠금에 비해 동시성이 크게 향상됩니다.

일반적인 상황에서는 대부분의 시나리오가 쓰기보다 더 많이 읽기 때문에 읽기-쓰기 잠금의 성능이 배타적 잠금보다 좋습니다. 쓰기보다 읽기가 많은 경우 읽기-쓰기 잠금은 배타적 잠금보다 더 나은 동시성과 처리량을 제공 할 수 있습니다.

Java 동시성 패키지에서 제공하는 읽기-쓰기 잠금의 구현은 ReentrantReadWriteLock 입니다.

public interface ReadWriteLock {
    /**
     * Returns the lock used for reading.
     *
     * @return the lock used for reading
     */
    Lock readLock();

    /**
     * Returns the lock used for writing.
     *
     * @return the lock used for writing
     */
    Lock writeLock();
}

읽기-쓰기 잠금 자체의 실현은 배타적 잠금보다 훨씬 복잡하므로 읽기-쓰기 잠금은 다음 상황에 더 적합합니다.

  • 고주파 읽기 작업, 상대적으로 빈도가 낮은 쓰기 작업
  • 읽기 작업에 사용되는 시간은 너무 짧지 않습니다. (그렇지 않으면 읽기-쓰기 잠금 자체의 복잡한 구현으로 인한 오버 헤드가 주요 소비 비용이됩니다.)

 

 

 

2. AQS 정보 :

1. 동기 장치의 의미 :

동기화 기는 잠금 및 기타 동기화 구성 요소의 기본 프레임 워크를 구축하는 데 사용되며 주로 int 멤버 변수를 사용하여 동기화 상태를 나타내고 FIFO 큐를 사용하여 대기 큐를 만듭니다. 그것의 서브 클래스는 동기화 상태를 변경 수정 AQS의 여러 가지 보호 방법을 다시 작성해야합니다 . 다른 방법은 주로 대기 및 차단 메커니즘을 구현합니다. 상태 업데이트는 getState, setState 및 compareAndSetState의 세 가지 메서드를 사용합니다 .

하위 클래스는 사용자 정의 동기화 구성 요소의 정적 내부 클래스로 정의하는 것이 좋습니다 . 동기화 기 자체는 동기화 인터페이스를 구현하지 않습니다. 사용자 정의 동기화 구성 요소 사용을위한 여러 동기화 상태 획득 및 해제 방법을 정의 할뿐입니다. 동기화 장치는 동기화 상태의 독점 획득을 지원합니다. 또한 동기화 상태의 공유 획득을 지원할 수 있으므로 다양한 유형의 동기화 구성 요소를 쉽게 구현할 수 있습니다.

동기화 기는 잠금 (또는 동기화 구성 요소)을 실현하는 데 핵심적인 역할을하며, 동기화는 잠금을 실현할 때 집계되고 동기화 기는 잠금의 의미를 실현하는 데 사용됩니다. 둘 사이의 관계는 다음과 같이 이해할 수 있습니다. 잠금은 사용자 지향적이며 사용자와 잠금 사이의 인터페이스를 정의하고 구현 세부 정보를 숨 깁니다. 동기화 기는 잠금의 구현 자로서 구현을 단순화합니다. 잠금 및 동기화 상태 관리, 스레드 큐잉, 대기 및 깨우기와 같은 저수준 작업을 보호합니다 . 잠금 및 동기화 프로그램은 사용자와 구현 자의 관심 영역을 잘 분리합니다.

2. 요약 :

  1. 동기화 구성 요소 (잠금뿐만 아니라 CountDownLatch 등) 의 구현은 동기화 기 AQS에 따라 달라집니다 . 동기화 구성 요소의 구현에서 AQS를 상속하는 정적 메모리 클래스를 정의하려면 AQS를 사용하는 것이 좋습니다.
  2. AQS는 설계를위한 템플릿 방식채택하고 있으며 AQS 의 보호 된 수정 방식은 AQS를 상속받은 서브 클래스에 의해 재 작성 및 구현되어야하며, AQS의 서브 클래스 의 메소드가 호출되면 재 작성된 메소드가 호출됩니다.
  3. AQS는 동기화 상태 관리, 스레드 큐잉, 이러한 저수준 작업 대기 및 깨우기를 담당하는 반면 Lock과 같은 동기화 구성 요소는 주로 동기화 의미론달성하는 데 중점을 둡니다 .
  4. AQS 방법을 다시 작성할 때 AQS에서 제공하는 방법을 사용 하여 동기화 상태를 수정하십시오.getState(),setState(),compareAndSetState()

3. AQS에서 제공하는 템플릿 방법은 세 가지 범주로 나눌 수 있습니다.

  1. 동기화 상태의 독점 획득 및 해제
  2. 동기화 상태의 공유 획득 및 해제;
  3. 동기화 대기열에서 대기중인 스레드의 상태를 쿼리합니다.

4. 동기화 구성 요소실현 각도 :

4.1 동기화 컴포넌트 구현 자의 관점 :

다시 쓰기 가능한 메서드를 통해 : exclusive : tryAcquire () (동기화 상태 독점 획득), tryRelease () (동기화 상태 독점 해제);

Shared  : tryAcquireShared () (동기화 상태의 공유 획득), tryReleaseShared () (동기화 상태의 공유 해제);

4.2, AQS의 각도

AQS의 경우 AQS가 true와 false에 대해 다른 작업을 수행하므로 구성 요소에서 반환 한 true와 false 만 동기화하면됩니다. True는 현재 스레드를 고려하여 동기화 된 구성 요소를 성공적으로 가져 와서 직접 반환합니다. AQS는 또한 현재 스레드를 동기화 된 큐에 삽입하는 것과 같은 일련의 메소드를 수행합니다.

일반적으로 동기화 구성 요소는 AQS 방식을 다시 작성하여 표현하려는 동기화 의미를 달성하고 AQS는 동기화 구성 요소가 표현한 참과 거짓 만 있으면됩니다. AQS는 참과 거짓의 서로 다른 상황에 대해 다른 처리를 수행합니다. 기본 구현에 대해서는 이 기사를 읽을 수 있습니다 . (자세한 설명은 아래 참조)

5. AQS에 대한 깊은 이해

5.1. 독점 잠금 :

  • void acquire (int arg) : 동기화 상태를 독점적으로 획득하고, 획득에 실패하면 동기화 대기열을 삽입하여 대기합니다.
  • void acquireInterruptibly (int arg) : 취득 메서드와 동일하지만 동기 큐에서 대기 할 때 인터럽트를 감지 할 수 있습니다.
  • boolean tryAcquireNanos (int arg, long nanosTimeout) : acquireInterruptibly를 기준으로 타임 아웃 대기 기능이 추가됩니다. 타임 아웃 기간 내에 동기화 상태를 얻지 못하면 false를 반환합니다.
  • boolean release (int arg) : 동기화 상태를 해제합니다.이 메서드는 동기화 대기열의 다음 노드를 깨 웁니다.

공유 잠금 :

  • void acquireShared (int arg) : 동기화 상태의 공유 획득, 배타적 유형과의 차이점은 여러 스레드가 동시에 동기화 상태를 획득한다는 것입니다.
  • void acquireSharedInterruptibly (int arg) : acquireShared 메서드를 기반으로 인터럽트에 응답하는 기능을 추가합니다.
  • boolean tryAcquireSharedNanos (int arg, long nanosTimeout) : acquireSharedInterruptibly를 기준으로 대기 시간 제한 기능을 추가했습니다.
  • boolean releaseShared (int arg) : 공유 릴리스 동기화 상태

5.2. 동기화 대기열 :

AQS의 동기화 대기열은 체인 방식으로 구현됩니다 .

AQS 에는 몇 가지 속성 이있는 정적 내부 클래스 Node 가 있습니다.

volatile int waitStatus // Node status
volatile Node prev // 현재 노드의 선행 노드 / 스레드
휘발성 노드 next; // 현재 노드의 후속 노드 / 스레드
휘발성 스레드 스레드; // 동기화 큐에 대한 스레드 참조
Node nextWaiter; // 대기 대기열의 다음 노드

분명히 이것은 양방향 대기열 입니다.

간단한 요약:

  1. , AQS, 노드 및 기타 정보 대기 상태의 내부 정적 클래스 노드는 노드의 데이터 구조 ;
  2. 동기화 대기열은 양방향 대기열이며 AQS는 헤드 및 테일 포인터를 보유하여 동기화 대기열을 관리합니다 .

5.3, 배타적 잠금

노드는 어떻게 팀에 들어오고 나가나요? 사실 이것은 잠금 을 획득하고 해제하는 두 가지 작업 , 즉 대기열에 넣기 작업을위한 잠금을 획득하지 못한 것과 대기열에서 빼기 작업을위한 잠금을 성공적으로 획득하는 작업에 해당합니다 .

독점 잠금 획득 () :

  • 성공 : 메서드가 종료되고 반환됩니다.
  • 실패 : 먼저 addWaiter ()를 호출 하고 ( CAS 끝이 삽입 됨 ) acquireQueued () 메서드 를 호출합니다.

독점 잠금 해제 해제 () :

위의 두 가지를 요약 할 수 있습니다.

  1. 스레드는 잠금을 획득하지 못하고 스레드는 대기열에 넣기 작업을 위해 노드로 캡슐화됩니다. 핵심 메서드는 addWaiter () 및 enq ()입니다. 동시에 enq ()는 동기화의 헤드 노드 초기화를 완료합니다. 큐 및 CAS 작업 실패의 재시도 ;
  2. 스레드 획득 잠금은 회전 프로세스입니다. 현재 노드의 선행 노드가 헤드 노드이고 동기화 상태를 성공적으로 획득 한 경우에만 노드가 대기열에서 제외됩니다. 즉, 노드가 참조하는 스레드가 잠금을 획득합니다. 조건이 충족되지 않으면 LookSupport.park () 메서드를 호출하여 스레드를 차단합니다 .
  3. 잠금이 해제되면 후속 노드가 깨어납니다.

일반적으로 말하면 : 동기화 상태를 획득 할 때 AQS는 동기화 대기열을 유지하고 동기화 상태를 획득하지 못한 스레드는 회전을 위해 대기열에 추가됩니다. 대기열을 제거 (또는 회전 중지)하는 조건은 선구자가되는 것입니다. node는 헤드 노드이고 동기화 상태가 성공적으로 획득되었습니다. 동기화 상태를 해제 할 때 동기화 기는 unparkSuccessor () 메서드를 호출하여 후속 노드를 깨 웁니다.

5.4. 공유 잠금 :

공유 잠금 획득 acquireShared () :

논리는 배타적 잠금 획득과 거의 동일하며, 스핀 과정 에서 빠져 나가는 조건 은 현재 노드의 선행 노드가 헤드 노드이고 tryAcquireShared (arg)의 반환 값이 크거나 같다는 것입니다. 동기화 상태를 성공적으로 얻으려면 0으로 설정합니다 .

공유 잠금 releaseShared ()의 해제 :

배타적 잠금 해제 프로세스와는 약간 다릅니다. 공유 잠금 해제 프로세스에서 여러 스레드의 동시 액세스를 지원할 수있는 동시 구성 요소의 경우 여러 스레드가 안전하게 동기화 상태를 해제 할 수 있도록 보장해야합니다. 여기서 사용되는 CAS 보증. CAS 작업이 실패했습니다. 계속합니다. 다음주기에서 다시 시도하십시오.

 

추천

출처blog.csdn.net/ScorpC/article/details/113857403