동기화(synchronization)와 락(Lock)

2024. 12. 11. 09:25·Language/Java
목차
  1. 동기화(synchronization)와 락(Lock)
  2. 고유 락(Intrinsic Lock)
  3. 고유락 재진입 (Reentrancy)
  4. 구조적 락(Structured Lock)
  5. 명시적 락(Explicit Lock)
  6. 가시성(Visibility)
반응형

동기화(synchronization)와 락(Lock)

Java에서 동기화는 여러 스레드가 공유 자원에 동시 접근할 때 발생할수 있는 문제를 방지하는데 사용된다.

Lock은 동기화를 위한 방법 중 하나로, 데이터의 일관성과 무결성을 유지할수 있다.

 

Lock 의 목적

  • 상호 배제(Mutual Exclusion) : 한번에 하나의 스레드만 자원에 접근할 수 있도록 보장
  • 교착 상태(Deadlock) 방지 : 올바른 락 획득 순서를 유지하여 교착상태 방지
  • 경쟁 상태 방지 : 여러 스레드가 동시에 자원에 접근하여 발생하는 예기치 않은 결과를 방지

 

 

 

 

고유 락(Intrinsic Lock)

Java에서 모든 객체는 하나의 고유 락을 가지고 있다.

이는synchronized 키워드를 사용할 때 자동으로 사용된다.

java
닫기
public class IntrinsicLockExample {
// 동기화된 메서드
public synchronized void synchronizedMethod() {
System.out.println(Thread.currentThread().getName() + ": Enter synchronizedMethod");
// 간단한 작업 시뮬레이션
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ": Exit synchronizedMethod");
}
public static void main(String[] args) {
IntrinsicLockExample example = new IntrinsicLockExample();
// 두 개의 스레드를 생성하여 같은 메서드를 호출
Thread thread1 = new Thread(() -> example.synchronizedMethod(), "Thread-1");
Thread thread2 = new Thread(() -> example.synchronizedMethod(), "Thread-2");
thread1.start();
thread2.start();
}
}

synchronizedMethod는 synchronozed 키워드로 동기화 되어 있다.

Thread-1 과 Thread-2가 동시에 synchronizedMethod를 호출하려고 할 때,

Thread-1이 먼저 고유 락을 획득하고, synchronizedMethod에 들어간다.

Thread-2는 Thread-1이 고유 락을 해제할 때까지 대기한다.

Thread-1이 메서드를 종료하고 락을 해제하면, Thread-2가 고유락을 획득하고 메서드에 들어간다.

 

 

 

 

고유락 재진입 (Reentrancy)

한 스레드가 이미 특정 객체의 고유락을 획득하고 있는 상태에서,

다시 그 고유락을 필요로 하는 synchronized블록이나 메서드에 진입하려고 할 때,

해당 스레드는 블록되지 않고 정상적으로 진입할 수 있다.

java
닫기
public class ReentrantLockExample {
// synchronized 메서드
public synchronized void outerMethod() {
System.out.println(Thread.currentThread().getName() + ": Enter outerMethod");
innerMethod(); // 동일한 객체의 다른 synchronized 메서드 호출
}
// synchronized 메서드
public synchronized void innerMethod() {
System.out.println(Thread.currentThread().getName() + ": Enter innerMethod");
// 추가적인 작업 시뮬레이션
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ": Exit innerMethod");
}
public static void main(String[] args) {
ReentrantLockExample example = new ReentrantLockExample();
Thread thread = new Thread(example::outerMethod, "Thread-1");
thread.start();
}
}

Thread-1이 outerMethod를 호출한다. 이는 synchronized메서드로, ReentrantLockExample 객체의 고유 락을 획득한다.

outerMethod안에서 innerMethod를 호출한다. 이것도 synchronized메서드이므로, 동일한 객체의 고유락을 필요로 한다.

이때, Thread-1은 이미 ReentrantLockExample 객체의 락을 획득했기 때문에, 별다른 과정 없이 innerMethod에 진입할 수 있다.

 

만약 재진입을 허용하지 않는다면 어떻게 될까?

스레드가 이미 락을 가지고 있는상태에서 다시 락을 획득하려고 할 때

자신이 소유한 락을 획득하려고 시도하면서 무한히 대기하게 되므로  교착상태에 빠질 수 있다.

 

위의 코드에서 innerMethod의 Lock이 해제되기 전에 outerMethod의 Lock을 해제할 수 있을까?

재진입 락의 특성때문에 innerMethod의 락이 해제되기 전에는 outerMethod의 락을 해제할 수 없다.

 

 

 

구조적 락(Structured Lock)

구조적 락(구조적 동기화)은 락을 얻고 해제하는 범위가 명확히 정의되어 이쓴 방식이다.

예시와 같이 구조적 락은 보통 synchronized 키워드로 구현되며 자동으로 락의 획득과 해제를 관리한다.

 

java
닫기
public class StructuredLockDemo {
private final Object lockA = new Object();
private final Object lockB = new Object();
public void demonstrateStructuredLock() {
synchronized (lockA) {
System.out.println("Lock A acquired");
synchronized (lockB) {
System.out.println("Lock B acquired");
// 여기서 lockB를 먼저 해제해야 함
} // Lock B 해제
// Lock A는 블록의 마지막에 자동으로 해제됨
} // Lock A 해제
}
}

위와 같은 예시에서 블록 단위로 lock의 획득 및 해제가 일어나므로

A 획득 -> B획득 -> B해제 -> A해제는 가능하지만

A 획득 -> B획득 -> A해제 -> B해제는 불가능하다.

 

이를 가능하게 하기 위해서는 명시적 락을 사용하면 된다.

 

 

 

 

명시적 락(Explicit Lock)

명시적 락은 Lock 인터페이스로 구현되며, 개발자가 직접 락의 획득과 해제를 관리해야 한다.

아래는 명시적 락 중 대표적인 ReentrantLock 클래스에 대한 예제이다.

java
닫기
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockDemo {
private final ReentrantLock lockA = new ReentrantLock();
private final ReentrantLock lockB = new ReentrantLock();
public void demonstrateReentrantLock() {
lockA.lock();
try {
System.out.println("Lock A acquired");
lockB.lock();
try {
System.out.println("Lock B acquired");
} finally {
lockA.unlock(); // 먼저 획득한 A 락 먼저 해제
}
} finally {
lockB.unlock(); // 나중에 획득한 B 락 나중에 해제
}
}
}

 

lockA.lock()을 통해 락을 획득한다.

ㄴlockA를 성공적으로 획득하면 다른 스레드는 lockA에 대한 락을 획득할 수 없다.

lockA를 획득한 후 lockB.lock()을 통해 락을 획득한다.

ㄴlockB도 마찬가지로 다른 스레드는 lockB에 대한 락을 획득할 수 없다.

lockA.unlock()을 통해 lockA에 대한 락을 lockB보다 먼저 해제한다.

이 후, lockB.unlock()을 통해 lockB에 대한 락을 해제한다.

 

 

명시적 락의 주요 메서드

  • lock() : 락을 획득할 때까지 현재 스레드 대기
  • lockInterrupteibly() : 인터럽트가 가능한 방식으로 락 획득
  • tryLock() : 락을 시도하고 즉시 성공하면 true, 아니면 false 반환
  • tryLock(long time, TimeUnit unit) : 주어진 시간내에 락을 시도하고 성공하면 true, 아니면 false 반환
  • unlock() :  락을 해제

 

💡 주요 명시적 락 클래스
- ReentrantLock : 재진입 가능한 락을 제공. 동일한 스레드가 여러번 락 획득할 수 있음
- ReadWriteLock : 읽기/쓰기 작업을 구분하여 여러 스레드가 동시에 읽기 작업을 수행할 수 있도록 함
- StampedLock : ReadWriteLock의 대안으로 낙관적 읽기 잠금을 제공. 더 높은 성능.

 

 

 

 

 

가시성(Visibility)

동시성 프로그램의 이슈 중 하나는 가시성이다. 값을 사용한 다음 블록을 빠져나가고 나면 다른 스레드가 변경된 값을 즉시 사용할 수 있게 해야 한다는 의미이다.

 

자바에서는 스레드가 락을 획득하는 경우 그 이전에 쓰였던 값들의 가시성을 보장한다.

이는 고유 락 뿐만 아니라 ReentrantLock 같은 명시적인 락에서도 똑같이 적용된다.

 

 

 

더보기

참고

 https://mint723dev.tistory.com/71

 

고유 락 (Intrinsic Lock)

자바의 모든 객체는 락(lock)을 갖고 있다모든 객체가 갖고 있으므로 고유 락(intrinsic lock)이라고 하며, 모니터처럼 동작한다고 하여 모니터 락(monitor lock), 혹은 그냥 모니터(monitor)라고 한다 예제

mint723dev.tistory.com

 

https://velog.io/@tkdtkd97/Java-%EA%B3%A0%EC%9C%A0-%EB%9D%BD-Intrinsic-Lock

 

Java 고유 락 (Intrinsic Lock)

자바의 모든 객체는 락(lock)을 갖고 있다.모든 객체가 갖고 있으니 고유 락(intrinsic lock), 모니터처럼 동작한다고 하여 모니터 락(monitor lock), 혹은 그냥 모니터(monitor)라고도 한다.동시성 문제를 해

velog.io

https://brunch.co.kr/@kd4/156

 

[읽고서] 자바 고유락과 Synchronization

기술 블로그 리뷰 | Java는 크게 3가지 영역의 메모리 영역을 가지고 있습니다. static 영역 Java 클래스 파일은 크게 필드(field), 생성자(constructor), 메소드(method)로 구성됩니다. 그중 필드 부분에서 선

brunch.co.kr

 

 

728x90
반응형

'Language > Java' 카테고리의 다른 글

record 클래스 (Java 14)  (0) 2024.12.13
직렬화(Serialization)  (0) 2024.12.12
Java의 스레드(Thread)  (0) 2024.12.10
Error & Exception  (1) 2024.12.09
가비지 컬렉션 (GC; Garbage Collection)  (0) 2024.12.08
  1. 동기화(synchronization)와 락(Lock)
  2. 고유 락(Intrinsic Lock)
  3. 고유락 재진입 (Reentrancy)
  4. 구조적 락(Structured Lock)
  5. 명시적 락(Explicit Lock)
  6. 가시성(Visibility)
'Language/Java' 카테고리의 다른 글
  • record 클래스 (Java 14)
  • 직렬화(Serialization)
  • Java의 스레드(Thread)
  • Error & Exception
settong
settong
    250x250
  • settong
    개 발 자 국
    settong
  • 전체
    오늘
    어제
    • 전체보기 (202)
      • Computer Science (50)
        • Network (7)
        • Operating System (18)
        • Data Structure (9)
        • Database (11)
        • Algorithm (5)
      • Language (17)
        • Java (17)
        • Javascript (0)
        • Python (0)
      • Devops (20)
        • AWS (0)
        • Naver Cloud (16)
        • CICD (3)
        • 웹 서버 관리 (1)
      • Front (0)
        • React (0)
      • Backend (5)
        • Spring (5)
      • 코딩 테스트 정복기 (110)
        • 백준 (51)
        • 프로그래머스 (53)
        • 기타 (6)
      • etc (0)
      • 경제 상식 (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    ncp202
    lcs
    ncp200
    프로그래머스
    ncp
    Spring Boot
    백준
    백트래킹
    BFS
    집합
    다익스트라
    완전탐색
    github actions
    CI/CD
    Network
    다이나믹프로그래밍
    DFS
    분할정복
    벨만포드
    해시
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.2
settong
동기화(synchronization)와 락(Lock)

개인정보

  • 티스토리 홈
  • 포럼
  • 로그인
상단으로

티스토리툴바

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.