JAVA 동기화 메커니즘(2)

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까지 다양한 옵션을 제공합니다.

각 메커니즘은 고유한 특성과 장단점이 있으므로, 애플리케이션의 요구사항에 맞는 적절한 메커니즘을 선택하는 것이 중요합니다.

이상적인 동기화 전략:

  1. 가능한 한 공유 상태를 최소화하세요
  2. 불변(immutable) 객체를 선호하세요
  3. 상황에 맞는 적절한 동기화 메커니즘을 선택하세요
  4. 성능에 민감한 경우 벤치마크 테스트를 수행하세요

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다