800

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. 성능에 민감한 경우 벤치마크 테스트를 수행하세요

답글 남기기

Your email address will not be published. Required fields are marked *.

*
*