>Java >java지도 시간 >Java 동시성 알아보기: 잠금 최적화, ConcurrentHashMap, 잠금 분리

Java 동시성 알아보기: 잠금 최적화, ConcurrentHashMap, 잠금 분리

php是最好的语言
php是最好的语言원래의
2018-07-30 10:02:151892검색

잠금 성능 향상에 도움이 되는 몇 가지 제안


잠금 유지 시간 줄이기

필요한 경우에만 동기화하여 잠금 유지 시간을 크게 줄이고 잠금 충돌 가능성 및 동시성 향상

For 예를 들어, 동기화 동기화 잠금을 사용하고, 개체가 변수 상태를 공유해야 할 때 추가해 보세요. 전체 메서드 앞에 맹목적으로 동기화를 추가하는 대신 이 메서드를 호출하는 개체를 직접 잠그면 잠금 경쟁 가능성이 높아집니다.


읽기- 쓰기 잠금 분리가 배타적 잠금을 대체합니다

효율성을 높이기 위해 ReadWriteLock 읽기-쓰기 분리를 사용하는 것에 대해 이야기했는데,

이는 잠금 분리 전략으로 더욱 확장됩니다.

배타적 잠금 분리 기술에 대한 일반적인 참조 장면은 LinkedBlockingQueue 작업입니다. 이전에 말했듯이 연결 목록을 기반으로 구현된 무제한 작업 대기열입니다. take() 메서드와 put() 메서드는 각각 대기열의 프런트 엔드와 테일 엔드에서 작동하며 상호 보완적인 효과를 갖습니다. JDK에서는 구현 시 이 두 작업에 대해 두 개의 잠금이 제공됩니다.

예를 들어 put() 작업의 다중 스레드 실행에는 putLock에 대한 경쟁이 가장 많이 필요하고 take 작업은 리소스 잠금에 대해 경쟁합니다. 잠금에 대한 일회성 요청에 통합될 수 있으므로 애플리케이션 및 릴리스 작업 소비가 줄어듭니다.


예:

 /** Lock held by take, poll, etc */
    private final ReentrantLock takeLock = new ReentrantLock();

    /** Wait queue for waiting takes */
    private final Condition notEmpty = takeLock.newCondition();

    /** Lock held by put, offer, etc */
    private final ReentrantLock putLock = new ReentrantLock();

    /** Wait queue for waiting puts */
    private final Condition notFull = putLock.newCondition();

는 잠금 세분성을 줄이기 위해

for(int i=0;i<n;i++){
		synchronized(lock){}
		
	}

에 최적화되어 있습니다.

이 기술의 일반적인 응용 시나리오는 HashMap과 비교할 때 스레드로부터 안전하고 HashTable과 비교할 때입니다. 효율적인 동시성


ConcurrentHashMap 구현 원칙

jdk1.7과 jdk1.8의 ConcurrentHashMap 구현에는 큰 차이가 있습니다

ConcurrentHashMap의 기본 구조는 세그먼트 배열이고 기본 크기는 16이며 각 세그먼트 배열은 작은 HashMap으로 간주됩니다. 즉, Segment 배열은 연결된 목록과 배열을 사용하여 구현됩니다.

jdk 1.7은 분할 잠금을 기반으로 ConcurrentHashMap을 구현합니다
  • 예를 들어, 새로운 키-값 쌍을 맵에 삽입해야 하는 경우 먼저 키의 해시코드에 따라 어느 세그먼트에 삽입해야 하는지 알아낸 다음 이 세그먼트를 잠가서 넣기를 완료합니다. 여기서 세그먼트는 잠금 역할을 합니다.

    Segment 클래스는 ReentrantLock 클래스

    에서 상속되므로 잠금을 획득할 때 잠금을 직접 사용하지 않습니다. 왜냐하면 이 메서드는 잠금 획득에 실패할 경우 중단되기 때문입니다. 실제로는
  • spin lock
을 사용합니다. tryLock이 잠금을 획득하지 못한다면 이는 다른 스레드가 잠금을 점유하고 있다는 의미입니다. 이때 루프를 통해 tryLock을 통해 잠금이 다시 적용됩니다. 따라서 멀티스레딩에서는 삽입된 데이터가 세그먼트에 위치하지 않고 잠금 경쟁으로 인해 차단이 발생하지 않는 한 스레드 간에 진정한 동시성을 달성할 수 있습니다.

문제: 교차 세그먼트 작업이 필요한 경우, 즉 시스템이 전역 잠금을 획득해야 하는 경우 모든 세그먼트의 잠금을 성공적으로 획득해야만 각 세그먼트의 잠금 상태를 확인해야 합니다. 글로벌 정보를 얻을 수 있습니다. 예를 들어 ConcurrentHashMap의 size() 메소드에는 모든 하위 세그먼트의 잠금이 필요합니다

Java 동시성 알아보기: 잠금 최적화, ConcurrentHashMap, 잠금 분리

CAS
  • 를 기반으로 하는 ConcurrentHashMap의 JDK 1.8 구현

    jdk1.7의 ConcurrentHashMap의 최대 동시성은 세그먼트 수와 동일합니다. 동시성을 더욱 향상시키기 위해 jdk 1.8에서는 분할 잠금 솔루션을 포기하고 대규모 배열을 직접 사용했습니다. 동시에, 해시 충돌 시 주소 지정 성능을 향상시키기 위해 Java 8은 연결된 목록(주소 지정 시간 복잡도는 O(N))을 레드-블랙 트리(주소 지정 시간 복잡도는 O(N))로 변환합니다. 연결된 목록의 길이가 특정 임계값(8)을 초과합니다. 배열에 있는 키의 인덱스는 키의 해시 값과 배열 길이의 모듈로를 취하여 결정됩니다

넣기 작업의 경우 키에 해당하는 배열 요소가 null이면 다음으로 설정됩니다. CAS 연산을 통해 현재 값. Key에 해당하는 배열 요소(즉, 연결 리스트의 헤드 또는 트리의 루트 요소)가 null이 아닌 경우에는 동기화 키워드를 사용하여 해당 요소에 잠금을 적용한 후 작업을 수행합니다. Put 작업으로 인해 현재 연결된 목록의 길이가 특정 임계값을 초과하는 경우 연결된 목록을 트리로 변환하여 주소 지정 효율성을 향상시킵니다.

읽기 작업의 경우 휘발성 키워드로 배열을 수정하므로 배열의 가시성에 대해 걱정할 필요가 없습니다. 동시에 각 요소는 Node 인스턴스입니다(Java 7의 각 요소는 HashEntry입니다). 해당 키 값과 해시 값은 최종적으로 수정되며 수정 후에는 변경될 수 없습니다. 해당 값과 다음 요소에 대한 참조는 휘발성에 의해 수정되며 가시성도 보장됩니다. Java 동시성 알아보기: 잠금 최적화, ConcurrentHashMap, 잠금 분리

Size 작업: 각각의 대형 배열은 카운터를 유지 관리합니다. Put 메소드와 Remove 메소드는 모두 addCount 메소드를 통해 Map의 크기를 유지합니다. size 메소드는 addCount 메소드에 의해 유지되는 Map의 크기를 얻기 위해 sumCount를 사용합니다.

ConcurrentHashMap에서 전역 카운터를 사용하는 대신 각 배열에 카운터를 포함하는 이유는 ConcurrentHashMap의 동시성을 고려하기 위함이라는 점에 유의하는 것이 중요합니다. 이런 방식으로 카운터를 업데이트해야 할 때 전체 ConcurrentHashMap을 잠가야 합니다개수는 휘발성이므로 모든 업데이트가 다른 스레드에 즉시 표시됩니다

관련 기사:

Java 프레임워크 Bootstrap, jQuery, SpringMVC, Hibernate 고성능 동시성

Java 시스템의 높은 동시성 문제에 대한 솔루션

관련 동영상:

Java 동영상 튜토리얼

위 내용은 Java 동시성 알아보기: 잠금 최적화, ConcurrentHashMap, 잠금 분리의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

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