- 🔍 Java 동시성 제어와 테스트 로드맵 요약2025년 05월 23일 21시 47분 13초에 업로드 된 글입니다.작성자: do_hyuk728x90반응형
1. ReentrantLock 기본 개념과 사용 이유
🔑 개념
- ReentrantLock은 Java의 java.util.concurrent.locks 패키지에 속한 클래스입니다.
- synchronized 키워드보다 세밀한 락 제어가 가능하며, 재진입(reentrant)을 지원합니다.
✅ 장점
- 락 획득 시도 (tryLock)
- 락 대기 시간 지정 (tryLock(timeout, TimeUnit))
- 공정성 정책 설정 가능 (FIFO 순서 등)
✅ 기본 사용 예
ReentrantLock lock = new ReentrantLock(); lock.lock(); // 락 획득 try { // 임계 영역 } finally { lock.unlock(); // 락 해제 }
2. tryLock() vs lock() 차이점
메서드설명lock() 락을 얻을 때까지 무조건 기다림 (blocking) tryLock() 락을 즉시 얻지 못하면 false 반환 🔍 예제
if (lock.tryLock()) { try { // 락 성공 시 작업 수행 } finally { lock.unlock(); } } else { // 락 실패 시 바로 반환 또는 에러 처리 }
💡 실전 팁
- 빠른 실패 처리가 필요한 서비스에는 tryLock()이 유리함.
- 블로킹이 필요하다면 lock()이 적절.
3. ID별 락 관리 (ConcurrentHashMap)
🤔 문제
- new ReentrantLock()을 호출하면 매번 새 객체가 생성되어 동기화 효과가 없음.
✅ 해결법
- ID마다 고유한 락을 하나씩 유지 → 같은 ID 접근 시 동기화됨.
예제
private final ConcurrentMap<Long, ReentrantLock> lockMap = new ConcurrentHashMap<>(); private ReentrantLock getLock(Long id) { return lockMap.computeIfAbsent(id, key -> new ReentrantLock()); }
이렇게 하면 같은 ID의 요청은 같은 락을 공유하게 되어 동시성 제어가 제대로 작동합니다.
4. 동시성 테스트 설계
✅ 핵심 요소
- ExecutorService: 쓰레드 풀로 비동기 실행
- CountDownLatch: 모든 쓰레드 종료 대기
- CyclicBarrier: 모든 쓰레드 동시에 시작
예제
ExecutorService executor = Executors.newFixedThreadPool(10); CountDownLatch latch = new CountDownLatch(10); for (int i = 0; i < 10; i++) { executor.submit(() -> { try { // 테스트할 서비스 호출 } finally { latch.countDown(); } }); } latch.await(); // 모든 쓰레드 종료까지 대기 executor.shutdown();
5. 예외 처리와 테스트 검증
🎯 목적
- 동시 요청 중 하나만 성공 → 나머지는 409 Conflict 등으로 실패해야 함
예제 검증 코드
List<Throwable> exceptions = Collections.synchronizedList(new ArrayList<>()); ... for (Throwable e : exceptions) { assertInstanceOf(HttpClientErrorException.class, e); assertEquals(HttpStatus.CONFLICT, ((HttpClientErrorException) e).getStatusCode()); }
6. 메모리 누수 방지
🧠 문제
- ConcurrentHashMap<Long, ReentrantLock>은 락을 계속 보관하게 됨 → 장기적으로 메모리 누수 위험
🛠 해결 방안
- WeakHashMap: 키가 GC 대상이면 자동 제거됨 (단, 멀티스레드에 안전하지 않음)
- 커스텀 캐시: 사용 시간 기준 제거 (예: Caffeine, Guava Cache)
- 직접 제거 로직 추가:
// 락 사용 후 제거 (주의 필요) lockMap.remove(id);
728x90반응형'포트폴리오 > Bank' 카테고리의 다른 글
Java에서 ReentrantLock을 활용한 동시 입금 제어 및 실패 테스트 정리 (0) 2025.05.23 💼 프로젝트 과제 예제: “은행 계좌 관리 시스템” (1) 2025.05.21 댓글