RedissonMultiLock과 isHeldByCurrentThread()의 문제

coldrice99·2024년 12월 17일
0
post-thumbnail

1. 문제 상황

쿠폰 발급 기능에서 Redisson RedLock을 활용해 동시성 제어를 구현했다. 락 해제 시 중복 해제를 방지하기 위해 isHeldByCurrentThread()를 사용했지만, RedissonMultiLock에서 UnsupportedOperationException 예외가 발생했다.


2. 원인 분석

  1. RedissonMultiLock 특성

    • RedissonMultiLock은 여러 노드에 대한 락을 조합하여 사용하는 RedLock 구현체이다.
    • 개별 노드의 락을 관리하므로 단일 노드의 상태를 검사하는 isHeldByCurrentThread() 메서드를 지원하지 않는다.
  2. isHeldByCurrentThread() 호출 시 문제

    • isHeldByCurrentThread()RedissonLock에는 존재하지만, RedissonMultiLock에서는 지원되지 않는다.
    • 이를 호출할 경우 UnsupportedOperationException 예외가 발생한다.

발생한 코드

if (redLock.isHeldByCurrentThread()) { 
    redLock.unlock();
}

예외 로그

java.lang.UnsupportedOperationException: null  
    at org.redisson.RedissonMultiLock.isHeldByCurrentThread(RedissonMultiLock.java:490)

3. 해결 방법

핵심 전략:
RedissonMultiLock에서는 isHeldByCurrentThread()를 사용하지 말고,
트랜잭션 종료 시점에 unlock()만 호출하도록 한다.

최종 코드

TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
    @Override
    public void afterCompletion(int status) {
        try {
            redLock.unlock(); // 바로 unlock 호출
            System.out.println("Thread " + Thread.currentThread().getId() + " released lock.");
        } catch (IllegalMonitorStateException e) {
            System.out.println("Lock already released or not held by thread.");
        } catch (Exception e) {
            System.err.println("Unexpected exception during lock release: " + e.getMessage());
        }
    }
});

포인트:

  • isHeldByCurrentThread() 검사를 제거한다.
  • unlock() 호출 시 이미 해제된 락에 대한 예외(IllegalMonitorStateException)는 명확하게 처리한다.

4. 개선된 동작

  1. 불필요한 검증 제거

    • isHeldByCurrentThread() 대신 트랜잭션 종료 시점에 unlock()만 호출한다.
  2. RedissonMultiLock 호환성 유지

    • RedissonMultiLock의 제한사항을 준수하면서도 안전하게 락을 해제한다.
  3. 안정적인 예외 처리

    • 이미 해제된 락에 대한 unlock() 호출은 예외를 무시하도록 처리한다.

5. 학습한 점

  1. RedissonMultiLock의 한계

    • isHeldByCurrentThread()RedissonLock 전용 메서드이며, RedissonMultiLock에서는 지원되지 않는다.
  2. 중복 해제 방지 전략

    • 트랜잭션 종료 시점에만 unlock()을 호출해야 하며, 불필요한 검증 로직은 제거해야 한다.
  3. 예외 처리

    • unlock() 호출 시 이미 해제된 락에 대해 IllegalMonitorStateException이 발생할 수 있으므로 이를 적절히 처리한다.
profile
서두르지 않으나 쉬지 않고

0개의 댓글