인프런 커뮤니티 질문&답변

고래밥님의 프로필 이미지

작성한 질문수

자바 동시성 프로그래밍 [리액티브 프로그래밍 Part.1]

ReentrantLock API

ReentrantLock.lockInterruptibly() 질문

24.06.25 23:01 작성

·

155

0

먼저 강의 잘 듣고 있습니다.

 

강의 자료 202쪽에 보면 아래 그림과 같이 ReentrantLock.lockInterruptibly() 예제 코드가 작성되어 있는데요.

 

이 코드를 강의 소스코드 (LockInterruptiblyExample.java) 에 적용해 보았습니다. 적용한 코드는 아래와 같습니다.

 

public class LockInterruptiblyExample {
    public static void main(String[] args) {
        Lock lock = new ReentrantLock();

        Thread thread1 = new Thread(() -> {
            try {
                lock.lockInterruptibly(); // 락을 시도하며, 인터럽트가 들어오면 중단
                System.out.println("스레드 1이 락을 획득했습니다");
            } catch (InterruptedException e) {
                System.out.println("스레드 1이 인터럽트를 받았습니다");
            } finally {
                lock.unlock();
                System.out.println("스레드 1이 락을 해제했습니다");
            }
        });

        Thread thread2 = new Thread(() -> {
            try {
                lock.lockInterruptibly(); // 락을 시도하며, 인터럽트가 들어오면 중단
                try {
                    System.out.println("스레드 2가 락을 획득했습니다");
                } finally {
                    lock.unlock();
                    System.out.println("스레드 2가 락을 해제했습니다");
                }
            } catch (InterruptedException e) {
                System.out.println("스레드 2가 인터럽트를 받았습니다");
            }
        });

        thread1.start();
        thread2.start();
        thread1.interrupt();
//        thread2.interrupt();

        try {
            Thread.sleep(500);
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }


    }
}

 

이 코드를 실행했을 때, 아래 이미지와 같이 IllegalMonitorStateException 이 발생했는데요.

 

아마 try ~ catch ~ finally 블록에 의한 문제가 아닐까 싶습니다. 이 부분이 어떻게 동작하여 오류가 발생한 건지 궁금한데, 답변 해주시면 감사하겠습니다.

답변 1

0

정수원님의 프로필 이미지
정수원
지식공유자

2024. 06. 27. 17:20

위 코드에서 IllegalMonitorStateException가 발생하는 원인은 unlock 메서드가 락을 획득하지 않은 스레드에 의해 호출되기 때문인데요

ReentrantLock 객체의 unlock 메서드는 락을 획득한 스레드만이 호출할 수 있습니다.

위 코드에서는 thread1이 인터럽트될 때 락을 획득하지 않은 상태에서 thread1 이 finally 에서unlock을 호출하게 되어 예외가 발생하고 있습니다.

그래서 다음과 같이 할 수 있습니다.

Thread thread1 = new Thread(() -> {
    try {
        lock.lockInterruptibly(); // 락을 시도하며, 인터럽트가 들어오면 중단
        System.out.println("스레드 1이 락을 획득했습니다");
    } catch (InterruptedException e) {
        System.out.println("스레드 1이 인터럽트를 받았습니다");
    } finally {
        if (((ReentrantLock) lock).isHeldByCurrentThread()) {
            lock.unlock();
            System.out.println("스레드 1이 락을 해제했습니다");
        }
    }
});

즉 finally 에서 현재 스레드가 락을 획득한 경우에만 unlock 을 호출하도록 하는 것입니다.

고래밥님의 프로필 이미지
고래밥
질문자

2024. 06. 27. 17:47

thread1 이 lock.lockInterruptibly() 을 호출하기 전에 인터럽트 되었고, 곧바로 finally블록을 수행하면서 unlock을 시도(lock획득 안한 상태)하기 때문에 Exception이 발생한 것으로 이해했습니다.

답변 감사드립니다!