개발/JAVA

[JAVA] ReentrantLock 이란?

seopport 2023. 11. 1. 23:34
728x90
반응형

들어가기 앞서,

동기화를 위하여 synchronized 키워드를 사용하다 보니, 해당 키워드 말고 다른 것들은 무엇이 있을까 찾아보게 되었습니다. 그러다가 발견한 것이 ReentrantLock입니다. 해당 글에서는 아래 3가지 질문으로 구분 지어 작성해 보도록 하겠습니다.

 

  1. ReentrantLock 이란?
  2. ReentrantLock 사용하는 방법 및 예시
  3. ReentrantLock VS synchronized 의 차이점은 무엇이 있나요?

 

ReentrantLock Image
ReentrantLock Image

 

1. ReentrantLock 이란?

  • JAVA 에서 동시에 여러 개의 스레드가 접근하는 것을 제어하기 위한 동기화 메커니즘 중 하나입니다.
  • 수동으로 잠금 영역(Lock) 의 시작점과 끝점을 설정할 수 있는 객체입니다.
  • 즉, 유연한 동기화를 제공합니다.

 

2. ReentrantLock 사용하는 방법과 예시

  • ReentrantLock 변수를 선언합니다.
  • lock()을 호출하여 락을 획득하고, unlock()을 호출하여 락을 해제합니다.
  • 수동적으로 Lock 을 생성하고 해제해야 하기 때문에 finally 안에 unlock을 선언하는 것이 안전합니다.

 

import java.util.concurrent.locks.ReentrantLock;

public class Example {
    private ReentrantLock lock = new ReentrantLock();

    public void performTask() {
        lock.lock(); // 락 획득
        try {
            // 보호되어야 할 코드 영역
            // 임계 영역 (Critical Section)
        } finally {
            lock.unlock(); // 락 해제
        }
    }
}

 

3. ReentrantLock VS synchronized 의 차이점은 무엇이 있나요?

특성 ReentrantLock synchronized
Lock 의 소유 여부 획득한 스레드가 해당 Lock 을
다시 획득 가능합니다.
하나의 스레드가 이미 블록된 영역을
재진입 할 수 없습니다.
유연성 커스트마이징 및 제어 가능합니다. 상대적으로 덜 유연합니다.
조건 변수 지원 조건 변수(Condition)를 지원합니다. 조건 변수(Condition)를 지원하지 않습니다.
블로킹 유무 tryLock() 메서드를 사용하여
블로킹을 피할 수 있습니다.
블로킹이 발생합니다.
예외 처리 방식 예외 처리를 위한 유연성을 제공합니다. 예외처리 시 Lock 을 자동으로 해제합니다.

 

코드 사용 예시

synchronized 사용 예시

public class SynchronizedExample {
    private int count = 0;

    public synchronized void increment() {
        count++;
    }

    public synchronized int getCount() {
        return count;
    }
}

 

ReentrantLock 사용 예시

import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockExample {
    private int count = 0;
    private ReentrantLock lock = new ReentrantLock();

    public void increment() {
        lock.lock();
        try {
            count++;
        } finally {
            lock.unlock();
        }
    }

    public int getCount() {
        return count;
    }
}

 

Condition

특정 쓰레드가 객체의 락을 가진 상태로 오랜 시간을 보내지 않도록 하는 것 역시 공유 데이터를 보호하는 것만큼이나 중요합니다. 물론 특정 스레드에서 객체의 Lock을 오랫동안 가지지 않도록 하기 위한 메서드들이 있습니다.

 

wait()

해당 메서드를 사용하면 실행 중이던 스레드에서 객체의 락을 반납하여 waiting pool 이라는 곳에서 대기하도록 할 수 있습니다.

 

notify()

해당 메서드가 사용하면 다시 작업을 진행할 수 있을 때 Lock 을 다시 얻어서 재진행 할 수 있습니다.

그러나, 특정 스레드가 Lock을 얻도록 통제할 수 없습니다.

 

notifyAll()

waiting pool 에 있는 모든 스레드에게 통보가 갑니다. 그러나 단 하나의 스레드만 Lock을 얻을 수 있고, 나머지는 Lock을 기다리며 대기합니다.

 

ReentLock는 위의 함수들을 그대로 사용하지 않고 다음과 같이 매칭하여 사용합니다.

ReentrantLock synchronized
await() wait()
signal() notify()
signalAll() notifyAll()

 

 

마지막으로 예제를 보겠습니다. 코드는 ChatGPT를 활용하였습니다.

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class SimpleReentrantLockConditionExample {
    public static void main(String[] args) {
        Test test = new Test();
        Thread thread1 = new Thread(() -> test.print("Thread 1"));
        Thread thread2 = new Thread(() -> test.print("Thread 2"));

        thread1.start();
        thread2.start();
    }
}

class Test {
    private ReentrantLock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();
    private boolean isThread1Turn = true;

    public void print(String message) {
        lock.lock();
        try {
            while ((message.equals("Thread 1") && !isThread1Turn) || (message.equals("Thread 2") && isThread1Turn)) {
                condition.await();
            }
            System.out.println(message + " is printing.");
            isThread1Turn = !isThread1Turn;
            condition.signal();
        } catch (InterruptedException e) {
        	// ...
        } finally {
            lock.unlock();
        }
    }
}

/* 출력 결과

Thread 1 is printing.
Thread 2 is printing.
Thread 1 is printing.
Thread 2 is printing.
Thread 1 is printing.
... (이하 생략)

*/

 

 

참고 사이트 ( ChatGPT 포함 )

may-yun 님 블로그

 

[JAVA] synchronized VS Reentrant Lock (차이점)

상호 배제를 통한 동기화의 개념을 선행하고 보면 이해하는데 도움이 됩니다.현재 데이터를 사용하고 있는 해당 스레드를 제외하고 나머지 스레드들은 데이터에 접근 할 수 없도록 막는 개념Jav

velog.io

 

Catsbi’s DLog - ReentrantLock

 

ReentrantLock

synchronized와 같이 동기화를 제공하지만, 동기화의 시작과 끝을 지정할 수 있는 객체

catsbi.oopy.io

 

ReentrantLcok (Java Platform SE 8)

 

ReentrantLock (Java Platform SE 8 )

Acquires the lock only if it is not held by another thread at the time of invocation. Acquires the lock if it is not held by another thread and returns immediately with the value true, setting the lock hold count to one. Even when this lock has been set to

docs.oracle.com

 

JH's Develog - [JAVA] 자바 스레드 동기화(2) - ReentrantLock과 Condition

 

[JAVA] 자바 쓰레드 동기화(2) - ReentrantLock과 Condition

[JAVA] 자바 쓰레드 동기화(1) - synchronized, wait()/notify() 멀티 쓰레드 환경에서 Critical section 문제를 해결하기 위해서는 Critical section에 대해 쓰레드를 동기화 해야합니다. 자바에서는 아래 두 가지 방

jhkimmm.tistory.com

 

728x90
반응형