Skip to content

Commit 299fda5

Browse files
authored
feat: 15장 추가 (#9)
1 parent 45da835 commit 299fda5

File tree

1 file changed

+76
-0
lines changed

1 file changed

+76
-0
lines changed

study/ch15.md

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# 단일 연산 변수와 논블로킹 동기화
2+
## 들어가며
3+
- 여러 스레드가 동작하는 환경에서 넌블로킹 알고리즘은 락을 사용하지 않고 CAS 알고리즘을 통해 데이터의 안정성을 보장할 수 있음
4+
- 넌블로킹 알고리즘은 여러 스레드가 동일한 자료를 놓고 경쟁하는 과정에서 대기 상태에 들어가는 일이 없음 -> 스케줄링 부하 감소
5+
- 자바 5.0부터 AtomicInteger와 같은 단일 연산 변수(atomic variable)를 사용해 넌블로킹 알고리즘을 효율적으로 구현할 수 있음
6+
- 단일 연산 변수 = volatile 변수의 역할(가시성) + 단일 연산
7+
8+
## 1. 락의 단점
9+
- 공유 변수에 접근하려는 스레드가 락을 확보하여 동기화 한다면
10+
- 해당 변수에 독점적인 접근 권한을 갖게됨
11+
- 다른 스레드는 락을 확보해야만 변경 사항을 볼 수 있음
12+
- 락에 대한 경쟁이 심해지면 컨텍스트 스위칭 부하와 스케줄링 관련 지연 현상으로 성능 저하 발생
13+
- 락을 확보하지 못한 대기 중인 스레드는 실행을 멈추고 대기해야함
14+
- 대기 중인 스레드가 락을 확보한 스레드보다 우선 순위가 높음에도 불구하고 락이 해제될 때까지 기다리는 상황(우선 순위 역전) 발생 가능
15+
- volatile 변수는 락보다는 훨씬 가벼운 동기화 방법
16+
- 가시성은 보장되나 원자성은 보장되지 않음
17+
- 여러 개의 작업을 단일 연산으로 묶으려면 락을 사용해야 함
18+
> volatile 변수처럼 가볍고 댠일 연산 조건까지 충족한 방법은 없을까?🧐
19+
20+
## 2. 병렬 연산을 위한 하드웨어적인 지원
21+
- CAS(Compare And Swap) 알고리즘: 비교 후 치환
22+
- 원자성을 보장하는 단일 연산
23+
- CAS 알고리즘은 하드웨어적으로 지원되며, 자바 5.0부터 java.util.concurrent.atomic 패키지를 통해 제공됨
24+
- 락을 사용하면서 발생하는 여러 가지 활동성 문제를 피할 수 있음
25+
- 3개의 피연산자를 사용
26+
- 메모리 위치 V
27+
- 예상 값 A
28+
- 새로운 값 B
29+
- 동작 과정
30+
1. 메모리 위치 V의 현재 값을 읽음
31+
2. 현재 값이 예상 값 A와 같으면, 새로운 값 B로 바꿈
32+
3. 현재 값이 예상 값 A와 다르면, 아무 작업도 하지 않음. 대신 현재 V 값을 반환
33+
34+
## 3. 단일 연산 변수 클래스
35+
- 락을 사용할 때보다 더 나은 성능을 제공
36+
- 대기 상태에 들어가거나 스레드 스케줄링 관련 문제가 발생하지 않음
37+
- 동기화를 위한 하드웨어 기능을 직접적으로 활용하므로 경쟁이 발생하는 상황에서 훨씬 높은 확장성 제공
38+
- Number 클래스를 상속받지만 Interger, Long, Double와 같은 래퍼 클래스는 상속 받지 않음
39+
- 단일 연산 변수 클래스는 내부적으로 값을 변경하는 메서드를 제공
40+
- 래퍼 클래스는 불변 객체이므로 값을 변경할 수 없음
41+
- hashCode()와 toString() 메서드를 재정의하지 않음
42+
- 모든 인스턴스가 서로 다른 다름
43+
- 따라서 해시 값을 기반으로 하는 컬렉션 클래스에 키 값으로는 적절하지 않음
44+
- AtomicReference 클래스를 통해 두 개 이상의 값을 원자적으로 다룰 수 있음
45+
46+
## 4. 넌블로킹 알고리즘
47+
- 넌블로킹 알고리즘: 어떤 스레드라도 작업이 실패하거나 대기 상태에 들어가지 않는 알고리즘
48+
- 락 프리 알고리즘: 각 작업 단계마다 적어도 하나의 스레드는 항상 작업을 진행할 수 있는 알고리즘
49+
- CAS 연산을 독점적으로 사용하는 알고리즘을 올바르게 구현한 경우 넌블로킹 특성과 락 프리 특성 모두 만족
50+
- ABA 문제는 노드를 재사용하는 알고리즘에서 CAS 연산을 사용할 때 발생할 수 있는 문제
51+
- 스레드가 변수의 값을 읽고, 다른 스레드가 그 값을 변경한 후 다시 원래 값으로 바꾸는 상황
52+
- ABA 문제를 해결하기 위해 버전 번호를 사용하는 방법이 있음
53+
- 변수의 값과 함께 버전 번호를 저장하고, CAS 연산 시 예상 값과 예상 버전 번호를 함께 비교
54+
- AtomicStampedReference 클래스를 사용하면 버전 번호를 쉽게 관리할 수 있음
55+
56+
## 추가 내용 정리
57+
### LongAdder & LongAccumulator (Java 8+)
58+
- 특정 값을 매우 자주 증가시키는 상황에서
59+
- AtomicLong 사용 시 모든 스레드가 하나의 변수를 놓고 경쟁 -> CAS 실패 시 재시도 -> 성능 저하
60+
- LongAdder 및 LongAccumulator 사용 시 경쟁 감소
61+
- 분산 후 합산 방식
62+
- 공용 메모리 base와 스레드 별 메모리 cell 존재 (동적 스트라이핑)
63+
- base 변수를 CAS 방식으로 읽다가 업데이트가 실패하면 while문을 도는 것 대신 스레드별 cell 메모리에 별도의 연산 수행
64+
- 최종 값이 필요할 때만 모든 cell 합산
65+
- LongAdder
66+
- 오직 덧셈만 가능
67+
- 경쟁이 심한 상황에서 AtomicLong보다 선호
68+
- LongAccumulator
69+
- 사용자 정의 이항 연산을 적용 가능 (덧셈, 곱셈, 최댓값, 최솟값 등)
70+
- 스레드 내 또는 스레드 간의 누적 순서는 보장되지 않으므로 누적 순서가 중요하지 않은 경우에만 사용
71+
- 초기값 설정 가능
72+
73+
74+
75+
76+

0 commit comments

Comments
 (0)