synchronized 와 ReentrantLock

Dayon·2024년 4월 10일
1

CS공부

목록 보기
16/16
post-thumbnail

👍 주제 선정 이유

  • synchronized 와 ReentrantLock 이 무엇이고, 뭐가 다른지 알아보자.


🧶 Thread

  • 프로세스 내에서 실행되는 여러 흐름의 단위

  • 같은 자원을 공유할 수 있기 때문에 동시에 여러 가지 일을 같은 자원을 두고 수행할 수 있고, 이는 여러 스레드가 동시에 하나의 자원을 공유하고 있기 때문에 자원 공유에서 동시성 이슈가 발생한다.

    ⇒ 동시성 문제를 해결하기 위한 여러 방법중 Lock 에 대해 알아보자

  • 자바에서 멀티 스레드 환경에서 thread-safe 확보를 위해 synchronized 혹은 ReentrantLock 를 사용하는 방법이 대표적



1️⃣ synchronized

  • 한 번에 하나의 쓰레드만 객체에 접근할 수 있도록 객체에 Lock을 걸어서 데이터의 일관성을 유지하는 것
  • 메서드 synchronized는 호출될 때 자동으로 Lock 작업을 수행한다. Lock 작업이 완료될때까지 내용은

모니터를 사용하여 구현되는 동기화

synchronized 는 모니터와 "대기 및 알림" 또는 "신호 및 계속" 메커니즘과 연결된 대기 세트를 사용하여 수행된다.

Java Monitor

  • 여러 스레드가 객체로 동시에 객체로 접근하는 것을 막는다.
  • 자바의 각 개체는 스레드를 잠그거나 잠금 해제할 수 있는 모니터와 연결되어 있다.
  • 한 번에 하나의 Thread만 모니터에 대한 Lock을 보유할 수 있다. 해당 모니터를 잠그려고 시도하는 다른 스레드는 해당 모니터에 대한 잠금을 얻을 수 있을 때까지 차단된다.

사용법

1) 메서드에 Lock을 걸 때

class Test {
    int count;
    synchronized void bump() {     
        count++;
    }
    static int classCount;
    static synchronized void classBump() {
        classCount++;
    }
}

2) 특정 변수에 Lock을 걸 때

class BumpTest {
    int count;
    void bump() {
        synchronized (this) { count++; }  
    }
    static int classCount;
    static void classBump() {
        try {
            synchronized (Class.forName("BumpTest")) {
                classCount++;
            }
        } catch (ClassNotFoundException e) {}
    }
}
  • 단, 변수에 lock을 걸기 위해선 해당 변수는 객체여야 한다. int, long과 같은 기본형 타입에는 lock을 걸 수 없다.

  • wait() : 객체의 lock을 풀고 Thread를 해당 객체의 waiting pool 에 넣는다.

  • notify() : waiting pool 에서 대기중인 Thread 중 하나를 깨운다.

  • notifyAll() : waiting pool 에서 대기중인 모든 Thread를 깨운다.

    synchronized 로직은 JDK 내에 내장 되어 있고, C++ 언어로 구현이 되어있다. [오픈소스]

⭐️ notify()를 실행할때, 대기 중인 스레드(waitSet) 중에서 어떤 스레드가 선택되는지 보장할 수 없다.

⇒ 어느 부분이 Lock인지 알수 없다, 암시적 Lock



2️⃣ ReentrantLock

  • JAVA로 작성되어 JDK 1.5 부터 등장한 java.util.concurrent 패키지에 포함되어있다.
  • synchronized 과 동일한 의미와 기본동작을 갖지만 명시적으로 Lock 객체를 생성하며 스레드의 재진입성을 지원하는 더 확장된 Lock 이다.
  • 해당 Lock의 범위를 메서드 내부에서 한정하기 어렵거나, 동시에 여러 Lock을 사용하고 싶을 때 사용한다.

ReentrantLock이 많은 기능들을 제공함을 알 수 있다.

  • CAS(Comapre And Swap)으로 동시에 하나의 스레드만이 잠금을 획득할 수 있도록 상태값을 관리하여 스레드 안전을 보장합니다.

  • 경쟁에 실패해 대기중인 스레드는 작업이 일시 중단된 후 FIFO 대기열에 저장이 된다.

    • synchronized의 경우 Thread간의 lock을 획득하는 순서를 보장해주지 않았다.(unFair) , ReentrantLock은 Fair, unFair 모두 지원한다.
    • Fair Lock을 선택하면 대기열에 들어간 스레드 순으로 Lock 리소스에 접근
  • 동일한 스레드에 의해 최대 20억번 까지의 재귀적 잠금을 지원한다.

  • 직접적으로 Lock 객체를 생성하여 사용할 수 있습니다.

    public class ReentrantLock
    extends Object
    implements Lock, Serializable
  • 잠금 및 잠금 해제는 모두 수동으로 처리할 수 있다.

    • lock() 메서드를 사용할 경우 다른 스레드가 해당 lock() 메서드 시작점에 접근하지 못하고 대기하게 된다.
    • unlock() 메서드를 실행해야 다른 메서드가 lock을 획득할 수 있게 된다.
  • 잠금 시간 제한을 설정할 수 있다. 만료가 되면 해당 스레드를 건너뛰기 때문에 synchronized 에서 발생할 수 있는 교착 상태를 방지할 수 있다.

사용법

 class X {
   private final ReentrantLock lock = new ReentrantLock();
   // ...

   public void m() {
     lock.lock();  // block until condition holds
     try {
       // ... method body
     } finally {
       lock.unlock()
     }
   }
 }




정리

  • 코드는 더 길어지지만 JDK21 의 Virtual Thread를 사용하거나, Lock의 상세한 부분을 설정해주고 싶다면 ReentrantLock 을 사용하자

앞으로 해볼 것

  • 현재 진행중인 프로젝트에서 조회수 count하는 메서드에서 동시성 이슈를 전혀 고려하지 않았다! 수정할 것
  • Thread-safe 한 방식으로 Lock이 외에도 Volatile, Immutable Instance, ConcurrentHashMap 에 대해 공부해 볼 것

참고한 사이트

  1. https://www.baeldung.com/java-synchronize-static-variable-different-threads
  2. https://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.4.3.6
  3. https://stackoverflow.com/questions/3362303/whats-a-monitor-in-java
  4. https://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.1
  5. 남궁성의 자바의 정석 : https://github.com/castello/javajungsuk_basic [Chapter 13 : Thread ]
  6. https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/locks/ReentrantLock.html
  7. https://medium.com/javarevisited/synchronized-vs-reentrantlock-how-to-choose-cfb5306080e7
  8. https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/locks/ReentrantLock.html
profile
success is within reach, allow yourself time

0개의 댓글