ReentrantLock을 이용한 Thread 동기화

2023. 12. 26. 19:09·📘 Backend/Concurrency

📘 ReentrantLock

이번에 일을 하던 중 ReentrantLock을 사용해 Concurrency한 환경에서 여러 Thread간 안전하게 Lock을 사용 & 제공하며,

Interrupt 처리를 통해 Thread간 상호작용을 제어하고 Thread Safe한 기능의 개발을 위해 사용했었습니다.

완벽하게 숙지를 못한 상태에서 일단 사용한 것이므로 예시를 통해 ReentrantLock을 복습한 글입니다.


📕 Synchronization

ReentrantLock의 lock() & unLock() & tryLock() 을 이용해 Thread간 Race Condition을 방지하고,

lockInterruptibly() 를 사용해 Lock을 획득할 때 다른 Thread에 의해 해당 Thread가 Interrupte 되면 InterruptException을 발생 시킵니다.


📕 Interrupt 처리

아래 코드에서 쓰인 lockInterruptibly() 를 사용해서 Lock을 획득하고,

센서 동작중에 다른 Thread에 의해 Interrupt 되면 센서의 동작을 중단시키고 Lock을 해제 시킵니다.


📕 Dead Lock 방지

ReentrantLock은 데드락을 방지할 수 있는 기능이 있습니다..

락을 획득한 스레드가 다시 락을 획득할 수 있도록 허용하여 교착 상태를 방지할 수 있습니다.


📘 예시

📕 방에 움직임이 있을 때 조명을 계속 켜두는 Light Sensor Class

방에 움직임이 있으면 불을 계속 켜두는 센서가 있고,

더이상 움직임이 없을때 불을 끄기 위해 작동하는 TurnOffLight Thread가 있습니다.


TurnOffLights Thread는 Lock을 얻기위해 tryLock() 을 사용합니다.

Lock을 얻을 수 없으면 500ms 동안 프로세스가 지연되고, lastSignal Thread는 5초동안 Lock 획독을 차단합니다.

그 후, TurnOffLights Thread는 Lock을 얻고 조명을 끌 수 있게 되는 구조입니다.


따라서 이 경우, TurnOffLights Thread는 5초 동안 센서에 움직임이 감지가 되지 않는 경우,

조명을 끌 수 있도록 허용됩니다.


또한, 이전 신호를 차단하기 위해 lock.lockInturruptibly()를 사용합니다.

/**  
 * ReentrantLock : Concurrency 제어를 위한 Lock 객체  
 * lastSignal Thread : 마지막으로 수신된 신호를 추적하기 위한 Thread  
 */
 @Slf4j  
public class LightSensor {  
    private static volatile Lock lock = new ReentrantLock();  
    private static volatile Thread lastSignal = null;  
    private static Sensor sensor = new Sensor();  

    /**  
     *  Sensor Class     
     *  이전 신호를 무효화하고 현재 신호를 설정하는 invalidatePreviousSignalAndSetUpCurrent() 호출  
     *  작업 완료 후 Thread Lock 해제  
     */  
    private static class Sensor implements Runnable {  
        // 센서가 신호를 처리중인지 나타내는 상태 변수  
        private static Boolean preparing = false;  

        // 센서가 현재 작업을 처리중인지 확인하는 함수  
        public static Boolean isPreparing() { return preparing; }  

        @Override  
        public void run() {  
            log.info("Signal Send : {}", Thread.currentThread().getName());  

            try {  
                invalidatePreviousSignalsAndSetUpCurrent();  
                Thread.sleep(5 * 1500);  
            } catch (InterruptedException e) {  
                log.info("Signal interrupted : {}" + Thread.currentThread().getName());  
                return;  
            } finally {  
                lock.unlock();  
            }  
        }  

        /* 이전 신호를 무효화하고 현재 신호를 설정하는 함수 */        
        private static synchronized void invalidatePreviousSignalsAndSetUpCurrent() throws InterruptedException {  
            preparing = true;  

            // 마지막으로 수신된 신호가 있으면 해당 신호를 Interrupt 시킴  
            if (lastSignal != null) lastSignal.interrupt();  

            // 현재 스레드를 마지막으로 수신된 신호로 설정하고 Lock 시도  
            lastSignal = Thread.currentThread();  
            lock.lockInterruptibly();  
            preparing = false;  
        }  
    }  

    /**  
     * Light를 Off 시키는 클래스  
     * Sensor의 isPreparing이 False(처리중 X)면 Lock을 시도하여 불을 끔  
     * 작업 완료 후 Lock 해제  
     */  
    private static class TurnOffLights implements Runnable {  
        @Override  
        public void run() {  
            while (true) {  
                try {  
                    Thread.sleep(500);  
                } catch (InterruptedException e) {  
                    log.info("THread Interrupted : {}", this.getClass().getName());  
                    return;  
                }  

                if (!Sensor.isPreparing()) {  
                    if (lock.tryLock()) {  
                        try {  
                            log.info("Turn Off Lights");  
                            break;  
                        } finally {  
                            lock.unlock();  
                        }  
                    } else {  
                        log.info("Cannot Turn Off Lights Yet");  
                    }  
                } else {  
                    log.info("Cannot Turn Off Lights Yet");  
                }  
            }  
        }  
    }  

    /**  
     * 10번의 Loop를 돌아 40번의 광 센서 신호를 보냄  
     */  
    public static void main(String[] args) throws InterruptedException {  
        Thread turnOffLights = new Thread(new TurnOffLights());  

        for (int x=0; x<10; x++) {  
            new Thread(sensor).start();  
            new Thread(sensor).start();  
            new Thread(sensor).start();  
            new Thread(sensor).start();  
            Thread.sleep(250);  
        }  

        turnOffLights.join();  
    }  
}

결과값

18:40:21.783 [Thread-35] INFO com.bridge.thread.LightSensor -- Signal interrupted : {}Thread-35
18:40:21.784 [Thread-36] INFO com.bridge.thread.LightSensor -- Signal interrupted : {}Thread-36
18:40:21.784 [Thread-33] INFO com.bridge.thread.LightSensor -- Signal interrupted : {}Thread-33
18:40:22.043 [Thread-38] INFO com.bridge.thread.LightSensor -- Signal Send : Thread-38
18:40:22.044 [Thread-34] INFO com.bridge.thread.LightSensor -- Signal interrupted : {}Thread-34
18:40:22.042 [Thread-37] INFO com.bridge.thread.LightSensor -- Signal Send : Thread-37
18:40:22.046 [Thread-40] INFO com.bridge.thread.LightSensor -- Signal Send : Thread-40
18:40:22.046 [Thread-38] INFO com.bridge.thread.LightSensor -- Signal interrupted : {}Thread-38
18:40:22.047 [Thread-39] INFO com.bridge.thread.LightSensor -- Signal Send : Thread-39
18:40:22.047 [Thread-37] INFO com.bridge.thread.LightSensor -- Signal interrupted : {}Thread-37
18:40:22.048 [Thread-40] INFO com.bridge.thread.LightSensor -- Signal interrupted : {}Thread-40
저작자표시 (새창열림)

'📘 Backend > Concurrency' 카테고리의 다른 글

Critical Section & Synchronized with Lock  (2) 2024.01.06
Thread 간 Resource 공유 시 발생할 수 있는 문제  (3) 2024.01.06
Throughput Optimization - 처리량 최적화 & 성능 테스트 (Apache Jmeter)  (2) 2023.12.03
Image Processing - Latency Optimization (지연시간 최적화)  (0) 2023.12.03
Thread Blocking (Count Down Latch)  (1) 2023.11.28
'📘 Backend/Concurrency' 카테고리의 다른 글
  • Critical Section & Synchronized with Lock
  • Thread 간 Resource 공유 시 발생할 수 있는 문제
  • Throughput Optimization - 처리량 최적화 & 성능 테스트 (Apache Jmeter)
  • Image Processing - Latency Optimization (지연시간 최적화)
신건우
신건우
조용한 개발자
  • 신건우
    우주먼지
    신건우
  • 전체
    오늘
    어제
    • 분류 전체보기 (422)
      • 📘 Frontend (71)
        • Markup (1)
        • Style Sheet (2)
        • Dart (8)
        • Javascript (12)
        • TypeScript (1)
        • Vue (36)
        • React (2)
        • Flutter (9)
      • 📘 Backend (143)
        • Java (34)
        • Concurrency (19)
        • Reflection (1)
        • Kotlin (29)
        • Python (1)
        • Spring (42)
        • Spring Cloud (5)
        • Message Broker (5)
        • Streaming (2)
        • 기능 개발 (5)
      • 💻 Server (6)
        • Linux (6)
      • ❌ Error Handling (11)
      • 📦 Database (62)
        • SQL (31)
        • NoSQL (2)
        • JPQL (9)
        • QueryDSL (12)
        • Basic (4)
        • Firebase (4)
      • ⚙️ Ops (57)
        • CS (6)
        • AWS (9)
        • Docker (8)
        • Kubernetes (13)
        • MSA (1)
        • CI & CD (20)
      • 📚 Data Architect (48)
        • Data Structure (10)
        • Algorithm (8)
        • Programmers (17)
        • BaekJoon (5)
        • CodeUp (4)
        • Design Pattern (4)
        • AI (0)
      • ⚒️ Management & Tool (8)
        • Git (7)
        • IntelliJ (1)
      • 📄 Document (10)
        • Project 설계 (6)
        • Server Migration (3)
      • 📄 책읽기 (2)
        • 시작하세요! 도커 & 쿠버네티스 (2)
      • 🎮 Game (4)
        • Stardew Vally (1)
        • Path of Exile (3)
  • 블로그 메뉴

    • 링크

      • Github
    • 공지사항

    • 인기 글

    • 태그

      Lock #Thread #Concurrency
      GStreamer #Pipeline
      React #Markdown
    • 최근 댓글

    • 최근 글

    • hELLO· Designed By정상우.v4.10.0
    신건우
    ReentrantLock을 이용한 Thread 동기화
    상단으로

    티스토리툴바