6. StampedLock
Java 8에서 도입된 더 유연한 락 메커니즘입니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
import java.util.concurrent.locks.StampedLock;
public class StampedLockCounter {
private int count = 0;
private final StampedLock lock = new StampedLock();
public void increment() {
long stamp = lock.writeLock(); // 쓰기 락 획득 및 스탬프 반환
try {
count++;
} finally {
lock.unlockWrite(stamp);
}
}
public int getCount() {
long stamp = lock.tryOptimisticRead(); // 낙관적 읽기 시도
int currentCount = count;
if (!lock.validate(stamp)) { // 낙관적 읽기 실패시
stamp = lock.readLock(); // 읽기 락으로 전환
try {
currentCount = count;
} finally {
lock.unlockRead(stamp);
}
}
return currentCount;
}
}
|
cs |
7. 동기화 컬렉션
Java는 스레드 안전한 컬렉션 클래스도 제공합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class SynchronizedCollectionsExample {
public static void main(String[] args) {
// 동기화 래퍼 방식
Map<String, Integer> synchronizedMap =
Collections.synchronizedMap(new HashMap<>());
// 동시성 컬렉션 클래스
Map<String, Integer> concurrentMap = new ConcurrentHashMap<>();
// 성능 비교 코드
}
}
|
cs |
8. 샘플: 계좌 이체 시스템
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
public class BankTransferExample {
public static void main(String[] args) {
final Account account1 = new Account(1000);
final Account account2 = new Account(1000);
// 동시에 여러 이체 작업 실행
for (int i = 0; i < 10; i++) {
new Thread(() –> {
for (int j = 0; j < 100; j++) {
transfer(account1, account2, 10);
transfer(account2, account1, 10);
}
}).start();
}
}
// 데드락을 방지하는 계좌 이체 메소드
private static void transfer(Account from, Account to, int amount) {
// 항상 낮은 ID의 계좌부터 락 획득으로 데드락 방지
Account first = from.id < to.id ? from : to;
Account second = from.id < to.id ? to : from;
synchronized(first) {
synchronized(second) {
if (from.balance >= amount) {
from.balance –= amount;
to.balance += amount;
}
}
}
}
static class Account {
final int id = nextId();
int balance;
Account(int balance) {
this.balance = balance;
}
private static int idCounter = 0;
private static synchronized int nextId() {
return idCounter++;
}
}
}
|
cs |
9. 성능 최적화 기법
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
public class SynchronizationOptimization {
// 1. 락 분할 (Lock Splitting)
class SplitLockCounter {
private final Object readLock = new Object();
private final Object writeLock = new Object();
private int count = 0;
public void increment() {
synchronized(writeLock) {
count++;
}
}
public int getCount() {
synchronized(readLock) {
return count;
}
}
}
// 2. 락 스트라이핑 (Lock Striping)
class StripedMap<K, V> {
private static final int STRIPE_COUNT = 16;
private final Object[] locks;
private final Map<K, V>[] buckets;
@SuppressWarnings(“unchecked”)
public StripedMap() {
locks = new Object[STRIPE_COUNT];
buckets = new Map[STRIPE_COUNT];
for (int i = 0; i < STRIPE_COUNT; i++) {
locks[i] = new Object();
buckets[i] = new HashMap<>();
}
}
private int getStripe(K key) {
return Math.abs(key.hashCode() % STRIPE_COUNT);
}
public V get(K key) {
int stripe = getStripe(key);
synchronized(locks[stripe]) {
return buckets[stripe].get(key);
}
}
public void put(K key, V value) {
int stripe = getStripe(key);
synchronized(locks[stripe]) {
buckets[stripe].put(key, value);
}
}
}
}
|
cs |
10. 동기화 관련 일반적인 문제점과 해결책
데드락 회피 기법
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
public class DeadlockAvoidance {
// 타임아웃을 사용한 데드락 회피
public boolean transferWithTimeout(Account from, Account to, int amount, long timeout)
throws InterruptedException {
long startTime = System.currentTimeMillis();
long endTime = startTime + timeout;
while (System.currentTimeMillis() < endTime) {
if (from.lock.tryLock()) {
try {
if (to.lock.tryLock()) {
try {
if (from.balance >= amount) {
from.balance –= amount;
to.balance += amount;
return true;
} else {
return false;
}
} finally {
to.lock.unlock();
}
}
} finally {
from.lock.unlock();
}
}
// 잠시 대기 후 재시도
Thread.sleep(1);
}
// 타임아웃 – 실패
return false;
}
static class Account {
final Lock lock = new ReentrantLock();
int balance;
}
}
|
cs |
Java의 동기화 메커니즘은 단순한 synchronized
키워드부터 복잡한 StampedLock까지 다양한 옵션을 제공합니다.
각 메커니즘은 고유한 특성과 장단점이 있으므로, 애플리케이션의 요구사항에 맞는 적절한 메커니즘을 선택하는 것이 중요합니다.
이상적인 동기화 전략:
- 가능한 한 공유 상태를 최소화하세요
- 불변(immutable) 객체를 선호하세요
- 상황에 맞는 적절한 동기화 메커니즘을 선택하세요
- 성능에 민감한 경우 벤치마크 테스트를 수행하세요