ConcurrentHashMap
- 동시성에 최적화된 해시맵 구현체
- 여러 스레드가 동시에 데이터를 읽고 쓸 수 있도록 설계되어 있다
- 동시성 최적화 방식
- 세그먼트 락킹
- Java8 이전
- 해시맵이 여러 세그먼트로 나누어져 각 세그먼트마다 개별적인 락을 사용했다
- 하나의 세그먼트에서만 락이 걸려도 다른 세그먼트에 대한 접근이 여전히 가능했다
- Java8
- 세그먼트 락킹 대신 세분화된 락으로 변경했다
- 내부적으로 락 스트라이핑 기법을 사용하여 개별 버킷에 락을 걸지 않고, 필요한 경우에만 부분적으로 락을 사용한다
- Java8 이전
- 락 프리 읽기 (Lock-free Reads)
- 읽기 작업에는 락을 걸지 않고 진행한다
- 이는 데이터 일관성을 유지하면서도 높은 읽기 성능을 제공한다
- 읽기 작업이 수행될 때는 데이터 구조의 내부 상태가 변하지 않도록 보장하는 기술을 사용한다
- 세그먼트 락킹
- 동시성 문제 해결
- 쓰기 작업에서 충동 최소화하는 방법
- 여러 스레드가 동시에 데이터를 쓰는 상황에서는 세그먼트 락킹이나 부분적인 락을 사용해서 충돌을 최소화한다
- 락 프리 읽기와 CAS 연산을 통해 쓰기 작업이 필요한 경우에도 락을 최소화하여 성능 저하를 방지한다
- 읽기 작업의 높은 성능
- 읽기 작업이 락 없이 진행되므로, 여러 스레드가 동시에 데이터를 읽어도 성능 저하가 거의 없다
- 락 경합 감소
- 락을 세분화하여 사용함으로써, 여러 스레드가 동시에 작업할 때 락 경합이 줄어든다
- 이는 전통적인 단일 락 기반의 동기화보다 효율적이다
- 쓰기 작업에서 충동 최소화하는 방법
- 세그먼트 락킹
- 전체 해시맵을 여러 세그먼트로 나누고, 각 세그먼트에 별도의 락을 적용해서 동시성을 해결한다
- 세그먼트 락킹 동작 원리
- 해시맵 분할
- 여러 세그먼트로 분할하고 각 세그먼트는 독립적인 해시맵처럼 동작하며 자처적인 락을 가진다
- 데이터는 해시 값을 기반으로 세그먼트에 할당된다
- 따라서, 서로 다른 세그먼트에 위치한 데이터는 독립적으로 접근할 수 있다
- 세그먼트 락
- 각 세그먼트는 별도의 락을 가지고 있어서 다른 세그먼트에는 여향을 미치지 않는다
- 스레드가 데이터를 읽거나 쓸 때 해당 데이터가 속한 세그먼트 락만을 획득한다
- 이는 락 경합을 줄여주고 성능을 향상한다
- 읽기 작업
- 대부분의 읽기 작업은 락을 걸지 않고 수행한다
- 쓰기 작업
- 쓰기 작업은 해당 세그먼트의 락을 획득하고 수행한다
- 쓰기 작업동안 다른 세그먼트는 여전히 락이 걸리지 않으므로 다른 스레가 접근할 수 있다
- 해시맵 분할
- java8 이후 변경 사항
- 세그먼트 대신, 개별 버킷 수준에서 락을 적용으로 더 세밀한 락 관리가 가능하다
- 각 버킷에 대해 락을 적용하여 락 경합을 최소화하고 성능을 최적화한다
- Compare And Swap 연산
- CAS 연산을 통해 락을 사용하지 않고도 안전하게 값을 업데이트할 수 있다
- 이는 쓰기 작업에서는 성능을 향상한다
- 버킷과 세그먼트의 차이점
- 버킷
- 해시맵의 하나의 슬롯을 의미한다
- 해시 함수를 통해 계산된 인덱스에 저장되는 데이터 구조이다
- 각 버킷은 하나 이상의 키-쌍을 저장할 수 있다
- 여러 키가 같은 버킷에 저장될 수 있으며, 이를 처리하기 위해 체이닝이나 오픈 어드레싱 등의 충돌 해결 기법을 사용한다
- 세그먼트
- 해시맵을 여러 개의 독립적인 부분으로 나눈 것이다
- 각 세그먼트 독립적인 작은 해시맵처럼 동작하며, 자체적인 락을 가진다
- 버킷
- 세그먼트는 해시맵을 다시 작은 해시맵으로 나누어서 동시성을 해결하는 방식인 거 같다
- 버킷은 해시맵에 저장되는 기본 단위인 버킷 레벨에서 락을 관리하는 방법인 거 같다
- 세그먼트에서 버킷 방식으로 변경된 이유는 버킷으로 락을 관리하면 단위가 더 작아서 세그먼트보다 효율적으로 락 경합을 줄일 수 있어서다
- 그리고 구현도 버킷이 더 간단하고 효율적이다.
'TIL' 카테고리의 다른 글
임계영역을 알아보고 어떤 방법으로 구현할 수 있을까? (0) | 2024.06.18 |
---|---|
스레드가 코어 수보다 많아지면 어떤 문제가 생기고 어떻게 해결할 수 있나? (0) | 2024.06.17 |
프로세스가 있는데 왜 스레드가 필요한가? (0) | 2024.06.13 |
프록시 객체란 무엇인가? (0) | 2024.06.04 |
[React] helpers와 utils 디렉터리는 어떻게 구분할까? (0) | 2023.11.16 |