14. 동시 프로 그래 밍 의 ReentrantLock 공평 자물쇠 와 불공 정 자물쇠 원리 에 대한 상세 한 설명

프로필
자바 언어 에는 많은 네 이 티 브 스 레 드 안전 한 데이터 구조 가 있 습 니 다. 예 를 들 어 Array BlockingQueue, CopyOn Write Array List, LinkedBlockingQueue 등 스 레 드 안전 실현 방식 은 synchronized 키워드 가 아니 라 자바 util. concurrent. locks. ReentrantLock 을 통 해 이 루어 집 니 다.ReentrantLock 의 실현 은 내부 클래스 인 FairSync (공평자물쇠) 와 NonFairSync (불공 정 자물쇠) 를 바탕 으로 이 루어 졌 다.그 접근 성 은 Thread. currentThread () 를 바탕 으로 이 루어 집 니 다. 현재 스 레 드 가 실행 시퀀스 의 자 물 쇠 를 얻 었 다 면 실행 시퀀스 후의 모든 방법 으로 이 자 물 쇠 를 얻 을 수 있 습 니 다.
1. 공평자물쇠
  • 공평 과 불공평 한 자물쇠 의 대기 열 은 모두 자물쇠 내부 유 지 를 위 한 양 방향 링크 를 기반 으로 합 니 다. 표 노드 의 값 은 현재 자 물 쇠 를 요청 하 는 모든 스 레 드 입 니 다.공정 자 물 쇠 는 매번 팀 에서 순서대로 값 을 매 기 는 데 있다.
  • 자물쇠 의 실현 방식 은 다음 과 같은 몇 가 지 를 바탕 으로 한다. - 표 결점 노드 와 상태 state 의 volatile 키워드. -sum. misc. Unsafe. com pareAndSet 의 원자 조작 (부록 참조).

  • 2. 불공 정 자물쇠
    자 물 쇠 를 기다 리 는 과정 에서 새로운 스 레 드 가 자 물 쇠 를 얻 으 려 고 한다 면 자 물 쇠 를 직접 얻 을 확률 이 높다.
  • ReentrantLock 자 물 쇠 는 개발 자가 스스로 중단 위 치 를 설정 하지 않 는 한 스 레 드 를 중단 시 키 지 않 습 니 다.
  • ReentrantLock 은 자물쇠 안에 자전 하 는 것 처럼 보 이 는 코드 가 있 지만 자전 자물쇠 가 아 닙 니 다.
  • ReentrantLock 의 공평 과 불공평 한 자 물 쇠 는 모두 배타 적 자물쇠 에 속한다.

  • 2. ReentrantLock 의 접근 성 분석
    ReentrantLock 재 입 성 은 Thread. currentThread () 를 기반 으로 이 루어 집 니 다. 현재 스 레 드 가 잠 겨 있 으 면 이 스 레 드 의 모든 방법 으로 이 자 물 쇠 를 얻 을 수 있 습 니 다.ReentrantLock 의 잠 금 의존 도 는 NonfairSync 와 FairSync 두 가지 실현 클래스 만 있 고 그들의 잠 금 획득 방식 은 대동소이 하 다.
    //                 else if   
    protected final boolean tryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();
        if (c == 0) {
            ...
            //        
        }
        else if (current == getExclusiveOwnerThread()) {
            //      ,      。      。
            int nextc = c + acquires;
            if (nextc < 0)
                throw new Error("Maximum lock count exceeded");
            setState(nextc);
            return true;
        }
        return false;
    }
    

    관심 을 가 져 야 할 두 가지 값 이 있 습 니 다:
      	 //         
        private transient Thread exclusiveOwnerThread;
         -----------------         ----------------
        /**
         *     
         * 0:     -         
         * > 0:      ,                  
         *                。
         *           : volatile
         */
        private volatile int state;
    

    3. ReentrantLock 자물쇠 의 실현 분석
    1. 공평 자물쇠 와 불공평 자물쇠
    ReentrantLock 의 공정 자물쇠 와 불공 정 자 물 쇠 는 모두 Abstract Queued Synchronizer \ # acquire 에 게 요청 하 였 습 니 다.
    public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
    
  • try Acquire 는 추상 적 인 방법 으로 공평 과 불공평 한 실현 원리 이다.
  • addWaiter 는 현재 스 레 드 노드 를 대기 열 에 추가 합 니 다.공평 자 물 쇠 는 자물쇠 가 풀 린 후에 대기 열 에 따라 후속 값 을 엄 격 히 가 져 옵 니 다. 공평 자 물 쇠 는 신 진 스 레 드 에 큰 장점 이 있 습 니 다.
  • acquireQueued 는 여러 번 의 순환 에서 자 물 쇠 를 가 져 오 거나 현재 스 레 드 를 막 으 려 고 시도 합 니 다.
  • selfInterrupt 스 레 드 가 막 히 는 동안 중단 되면 Thread. currentThread (). interrupt () 를 호출 하여 현재 스 레 드 를 중단 합 니 다.
  • ReentrantLock 이 스 레 드 에 대한 차단 은 LockSupport. park (this) 를 기반 으로 합 니 다.(AbstractQueuedSynchronizer \ # parkAndCheckInterrupt, 선 결 조건 은 현재 노드 가 제한 적 으로 잠 금 을 가 져 오 는 데 실 패 했 습 니 다.)
  • 공정 한 잠 금과 비 공정 한 잠 금 은 잠 금 획득 에 있어 서 volatile 키 워드 를 수식 하 는 state 필드 에 사 용 됩 니 다. 이것 은 다 중 스 레 드 환경 에서 잠 금 의 획득 여 부 를 보장 하 는 핵심 입 니 다. 그러나 동시 다발 상황 에서 여러 스 레 드 가 state = = 0 을 읽 을 때 는 CAS 기술, CPU 의 원자 잠 금 기술 을 사용 해 야 합 니 다. CPU 를 통 해 공유 변수 에 잠 금 을 추가 하 는 형식 으로 데이터 변경 을 실현 할 수 있 습 니 다.원자 조작. volatile 과 CAS 의 결합 은 병발 선점 의 관건 이다.
    2. 공정 잠 금 FairSync
    공정 자물쇠 의 실현 메커니즘 은 매번 스 레 드 가 자 물 쇠 를 선점 할 때마다 대기 열 이 있 는 지 확인 하 는 것 입 니 다. 있 으 면 현재 스 레 드 는 다음 과 같은 절 차 를 수행 합 니 다.
    if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) {
        setExclusiveOwnerThread(current);
        return true;
    }
    

    이 중 hasQueued Predecessors 는 대기 열 이 있 는 지 확인 하 는 데 사 용 됩 니 다.
     public final boolean hasQueuedPredecessors() {
            Node t = tail; // Read fields in reverse initialization order
            Node h = head;
            Node s;
            return h != t &&
                ((s = h.next) == null || s.thread != Thread.currentThread());
        }
    

    3. 불공 정 잠 금 NonfairSync
    불공평 한 자 물 쇠 는 실현 할 때 무 작위 선점 을 여러 번 강조 한다.
    if (c == 0) {
        if (compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    

    공정 자물쇠 와 의 차 이 는 신 진 이 자 물 쇠 를 가 져 오 는 과정 에서 자 물 쇠 를 선점 할 수 있 는 기회 가 여러 번 있다 는 점 이다. 대기 열 에 가입 하면 공정 자물쇠 와 다 를 바 없다.
    4. ReentrantLock 자물쇠 의 방출
    ReentrantLock 잠 금 의 방출 은 한 단계 씩 방출 됩 니 다. 다시 들 어 갈 수 있 는 장면 에 서 는 장면 내 모든 잠 금 추가 방법 이 잠 금 을 풀 때 까지 기 다 려 야 현재 스 레 드 에 있 는 잠 금 이 풀 립 니 다. 방출 방식 은 간단 합 니 다. state 필드 에서 1 을 줄 이면 됩 니 다.
    protected final boolean tryRelease(int releases) {
        //  releases = 1
        int c = getState() - releases;
        if (Thread.currentThread() != getExclusiveOwnerThread())
            throw new IllegalMonitorStateException();
        boolean free = false;
        if (c == 0) {
            free = true;
            setExclusiveOwnerThread(null);
        }
        setState(c);
        return free;
    }
    

    5. ReentrantLock 대기 열 에 있 는 요소 의 각성
    현재 잠 금 이 있 는 스 레 드 가 잠 금 을 풀 고 불공평 한 잠 금 이 스 레 드 없 이 선점 되면 스 레 드 가 깨 어 나 는 절 차 를 시작 합 니 다. tryRelease 를 통 해 잠 금 을 풀 었 습 니 다. LockSupport. unpark (s. thread) 를 호출 하고 스 레 드 차단 을 종료 합 니 다.
    private void unparkSuccessor(Node node) {
        //              
        int ws = node.waitStatus;
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);
        Node s = node.next;
        // s h    Node,         Null 
        if (s == null || s.waitStatus > 0) {
            s = null;
            //     FIFO               Cancel Node
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)
                    s = t;
        }
        //     
        if (s != null)
            LockSupport.unpark(s.thread);
    }
    

    6. ReentrantLock 메모리 가시 성 분석
    try {
        lock.lock();
        i ++;
    } finally {
        lock.unlock();
    }
    

    volatile 키워드 수식 요소 i 를 사용 하지 않 아 도 i 는 병발 문제 가 없 음 을 알 수 있 습 니 다. volatile 은 자바 언어의 키워드 입 니 다. 기능 은 수 정 된 요소 (공유 변수) 를 보장 합 니 다.
  • 모든 프로 세 스 가 읽 을 때 이 프로 세 스에 있 는 공유 변수의 값 을 비우 고 메 인 메모리 에서 강제로 가 져 옵 니 다.
  • 모든 프로 세 스 가 기록 이 끝 났 을 때 공유 변수의 값 을 메 인 메모리 로 강제로 기록 합 니 다.
  • volatile 은 명령 정렬 에 관여 합 니 다.
  • volatile 은 JMM 규범 의 happen - before 원칙 을 실현 했다.
  • 좋은 웹페이지 즐겨찾기