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

Welcome to Seoyun Dev Log·2023년 4월 12일
2

JAVA

목록 보기
6/12

상호 배제를 통한 동기화의 개념을 선행하고 보면 이해하는데 도움이 됩니다.

Synchronized란?

현재 데이터를 사용하고 있는 해당 스레드를 제외하고 나머지 스레드들은 데이터에 접근 할 수 없도록 막는 개념

  • Java의 synchronized 키워드는 스레드 동기화(thread-safe)를 할 때 사용하는 대표적인 기법
  • synchronized 키워드가 붙은 메서드를 사용하려면 Lock 을 가지고 있어야 한다.

1) Synchronized 사용방법

  1. method: synchronized 함수를 만들어 사용한다.
    메서드 앞에 키워드
    : 함수에 synchronized를 걸면 그 함수가 포함된 해당 객체(this)에 lock을 거는것과 같습니다.

동기화 문제를 해결하는데 가장 간단하고 확실하면서 무식한 방법입니다.

여기서 무식하다 함은 synchronized로 인하여 객체에 포함된 다른 모든 synchronized의 접근 까지 lock이 걸리기 때문입니다.

public synchronized void method(){
// 코드
}
@Synchronized
fun syncMethod() = run {
}
  1. statements: synchronized block을 사용한다.
    인스턴스 사용: (메서드) { 구현 }
    블록안의 부분만 lock이 걸린다
    synchronized(obj)로 지정하게 되면 참조변수(obj) 객체의 lock을 사용
private Object obj = new Object();

public void exampleMethod(){ 
	synchronized(obj){
	//코드 
    }
}
  • method방식보다 좀 더 유연한 lock관리와 전략이 가능해집니다.
  • 네 가지 유형의 블록에 쓰인다.
    • 인스턴스 메소드
    • 스태틱 메소드
    • 인스턴스 메소드 코드블록
    • 스태틱 메소드 코드블록

2) 단점

  • Synchronized 키워드를 너무 남발하면 오히려 프로그램 성능저하를 일으킬 수 있습니다.
    (Synchronized 키워드를 사용하면 자바 내부적으로 메서드나 변수에 동기화를 하기 위해 block과 unblock을 처리하게 되는데 이런 처리들이 만약 너무 많아지게 되면 오히려 프로그램 성능저하를 일으킬수 있는 것)

ReentrantLock

특정 Statements에 대한 Locking을 관리하는 것을 볼 수 있다.

Syncronized를 사용할 경우 나의 Thread가 현재 멈추어 있는지? 기아 상태에 빠진건지? 내 Thread에서 해당 Locking을 들고 있는 건지? 등등의 정보를 얻을 수 없다. Syncronized 구문을 마주했을 때 누군가 해당 객체의 Locking을 Release 하기까지 무한히 기다리는 수 밖에 없다.
그래서 Reentrant는 다양한 기능을 제공한다.

  • 사용법은 간단하다 ReentrantLock 객체를 생성하여 사용
 private final ReentrantLock lock = new ReentrantLock();

Synchronized와 ReentrantLock의 차이점

lock을 좀 더 다양하게 관리하거나 복잡한 상황을 컨트롤해야할 수도있습니다.
특정 시간을 기다리고 안되면 무시하고 지나가야한다던지 lock을 잡는 순서를 보장해야하는 경우가 있을수 있죠
그런경우에 사용하는것이 ReentrantLock입니다.

synchronizedLock
임계영역범위메서드 안에 임계 영역의 시작과 끝이 존재해야한다.lock(), unlock()으로 시작과 끝을 명시하기 때문에 여러 메서드에 나눠서 작성할 수 있다.
경쟁상태(암시적인(implicit) lock): 스레드 진입권 획득 순서 보장 X명시적인(explicit) lock): 메서드를 호출함으로써 어떤 쓰레드가 먼저 락을 획득하게 될지 순서를 지정 O
  • ReentrantLock는 기존의 synchronized보다 확장된 기능을 가지고 있다.
  • Lock은 인스턴스에 한개 이상의 Condition을 지정할 수 있다. lockInterruptibly(), tryLock() 같은 편리한 제어 메서드를 사용할 수 있고, lock 획득을 기다리고 있는 쓰레드의 목록을 간편하게 확인할 수 있다.
  • synchronized는 간결한 코드로 임계 영역을 지정할 수 있다. 그리고 개발자의 실수로 lock을 해제하지 않아 문제가 생길 가능성이 없다. Lock을 사용할 경우 synchronized를 사용할 때와 달리 import 구문과 try-finally 문이 추가됨으로써 코드가 덜 간결해진다는 단점이 있다.

공정성이란?

공정한 Lock

  • 공정한 lock을 사용할 경우 경쟁이 발생했을 때 가장 오랫동안 기다린 쓰레드에게 lock을 제공
    (락을 요청하는 시간 간격이 긴 경우가 아니라면, 쓰레드를 공정하게 관리하는 것보다 불공정하게 관리할 때 성능이 더 우수하다. 그래서 일반적으로는 불공적 방식이 사용되는 듯 하다.)

📌 synchronized는 공정성을 지원하지 않는다. 반면 ReentrantLock은 생성자의 인자를 통해서 공정/불공정 설정을 할 수 있다.

  • 여기서 공정성은 모든 쓰레드가 자신의 작업을 수행할 기회를 공평하게 갖는 것을 의미한다. 공정한 방법에선 큐 안에서 쓰레드들이 무조건 순서를 지켜가며 lock을 확보한다. 불공정한 방법에선 만약 특정 쓰레드에 lock이 필요한 순간 release가 발생하면 대기열을 건너뛰는 새치기 같은 일이 벌어지게 된다.
    다른 쓰레드들에게 우선순위가 밀려 자원을 계속해서 할당받지 못하는 쓰레드가 존재하는 상황을 starvation(기아 상태)라 부른다. 이러한 기아 상태를 해결하기 위해 공정성이 필요하다.
public ReentrantLock() {
    sync = new NonfairSync();
}

public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

참고

차이점

profile
하루 일지 보단 행동 고찰 과정에 대한 개발 블로그

0개의 댓글