Thread는 하나의 프로세스 내에서 자원을 공유하면서 여러 개가 동시에 실행될 수 있다.
Race Condition (경쟁 조건)
Race condition은 둘 이상의 thread가 동시에 공유 자원 접근 시 발생한다.
Critical Section (임계 구역)
병렬 컴퓨팅에서 두 이상의 process 또는 thread가 동시 접근이 허용되지 않는 공유 자원에 접근하는 코드의 블록을 말함.
Mutual Exclusion (상호 배제)
Mutual exclusion이란 두 개 이상의 process 혹은 thread가 동시에 하나의 공유 자원으로 발생할 수 있는 race condition 문제를 해결하기 위해 어느 시점에서의 공유 자원 접근을 하나의 process 혹은 thread로 제한하는 것을 말한다.
Semaphore (세마포어)
세마포어 클래스는 스레드 간의 동기화를 위해 사용된다. 세마포어는 주어진 수의 허가를 관리하며, 스레드가 자원을 사용할 수 있도록 허가를 요청하고 반환하는 메커니즘을 제공 (동시에 여러 스레드가 임계영역에 접근할 수 있음)
Mutex (뮤텍스)
뮤텍스는 한 번에 하나의 스레드만 임계 구역에 접근할 수 있도록 보장하는 잠금 메커니즘이다.
Fair Lock (공정한 락)
ReentrantLock은 생성자에서 공정성(fairness)을 설정할 수 있습니다. 공정한 락은 FIFO (First-In-First-Out) 순서로 락을 대기하는 스레드에 락을 부여합니다. 즉, 먼저 락을 요청한 스레드가 먼저 락을 획득하게 된다.
* 기아 현상을 방지할 수 있음.
//공정한 락 설정
private final Lock lock = new ReentrantLock(true);
Unfair Lock (공정하지 않은 락)
공정하지 않은 락은 락을 요청하는 순서와 상관이 없고 현재 락을 해제한 스레드가 다시 락을 획득할 기회가 높다고 알려져 있다.
* 기아 현상이 발생할 수 있음
Synchronized
Multi Thread 환경에서 동기화를 구현하는 데 사용된다.
동기화는 여러 스레드가 동시에 하나의 공유 자원에 접근하지 못하도록 막아주는 중요한 개념
- synchronized 메서드: 메서드 전체를 동기화하여, 한 번에 하나의 스레드만 접근할 수 있도록 한다.
public synchronized long increaseAndGet(){
count = count + 1;
return count;
}
- synchronized 블록: 메서드 내의 특정 블록만 동기화하여, 필요한 부분만 보호할 수 있다.
public long increaseAndGet(){
synchronized (this) {
count = count + 1;
}
return count;
}
정리
참고
기아 현상 (Starvation)
특정 스레드가 자원에 접근할 기회를 계속해서 얻지 못하는 상황
모니터(Monitor)
모니터란?
모니터는 객체 기반의 동기화 메커니즘으로, 한 번에 하나의 스레드만 특정 코드 블록에 접근할 수 있도록 보장하는 구조
특징
- 객체 수준에서 관리 → 하나의 객체에 대해 하나의 모니터가 존재합니다.
- 자동 락(lock)과 해제 → synchronized 키워드를 사용하면 모니터가 자동으로 동작합니다.
- 조건 변수(Condition Variable) 지원 → wait(), notify(), notifyAll()로 대기 & 알림 기능을 제공함니다.
- 재진입 가능 (Reentrant) → 동일한 스레드가 같은 모니터를 여러 번 획득 가능 (synchronized 중첩 호출 가능)합니다
코드
class SharedResource {
public synchronized void method() { // 모니터 획득
System.out.println(Thread.currentThread().getName() + " 실행 중");
try {
Thread.sleep(1000); // 임계영역
} catch (InterruptedException e) {
e.printStackTrace();
}
} // 모니터 해제
}
public class MonitorExample {
public static void main(String[] args) {
SharedResource resource = new SharedResource();
Thread t1 = new Thread(() -> resource.method(), "Thread-1");
Thread t2 = new Thread(() -> resource.method(), "Thread-2");
t1.start();
t2.start();
}
}
- Thread-1이 실행되는 동안 Thread-2는 대기 상태가 됨. (synchronized로 인해 모니터가 잠김)
- Thread-1이 끝난 후 Thread-2가 실행됨.
뮤텍스 (Mutex)
뮤텍스란?
- 뮤텍스(Mutex, Mutual Exclusion)는 하나의 스레드만 공유 자원(임계 영역)에 접근할 수 있도록 보장하는 동기화 메커니즘입니다.
- 기본적으로 이진 락(Binary Lock)으로, 소유권(Ownership)이 있는 잠금(lock) 입니다.
- 하나의 스레드가 락을 획득하면, 반드시 같은 스레드가 해제해야 함
- 모니터는 자동 동기화 기능을 제공하지만, 뮤텍스는 명시적으로 락을 획득하고 해제해야 합니다.
특징
- 커널(Kernel) 혹은 사용자(User) 수준에서 구현 가능합니다.
- 락 기반 동기화 → lock()으로 획득하고 unlock()으로 해제해야 합니다.
- 재진입 가능 여부에 따라 다름 → 일부 뮤텍스는 재진입 불가능(한 스레드가 다시 락을 획득하려 하면 데드락 발생 가능)합니다.
- 스레드 간 공유 가능 → 프로세스 간 자원 공유 시 사용 가능 (예: POSIX Mutex)합니다.
- 자바에서 ReentrantLock을 통해 구현 가능합니다.
코드
import java.util.concurrent.locks.ReentrantLock;
class SharedResource {
private final ReentrantLock lock = new ReentrantLock();
public void method() {
lock.lock(); // 락 획득
try {
System.out.println(Thread.currentThread().getName() + " 실행 중");
Thread.sleep(1000); // 임계영역
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock(); // 락 해제
}
}
}
public class MutexExample {
public static void main(String[] args) {
SharedResource resource = new SharedResource();
Thread t1 = new Thread(() -> resource.method(), "Thread-1");
Thread t2 = new Thread(() -> resource.method(), "Thread-2");
t1.start();
t2.start();
}
}
- ReentrantLock을 사용하여 lock()으로 락을 획득하고, unlock()으로 해제.
- Thread-1이 락을 획득하면 Thread-2는 락을 획득할 수 없으므로 대기.
모니터 vs 뮤텍스
언제 모니터와 뮤텍스를 사용할까?
- 자바에서 synchronized 키워드만으로 동기화를 해결할 수 있을 때
- 단순한 메서드 동기화를 원할 때 (synchronized를 붙이면 자동으로 관리됨)
- 재진입(Reentrant) 기능이 필요한 경우
- 자바의 기본 동기화 방식 (wait(), notify(), notifyAll())을 활용하고 싶을 때
- 보다 정교한 동기화가 필요할 때 (ReentrantLock 사용)
- 락을 명시적으로 획득/해제하고 싶을 때
- 조건 변수(Condition Variable)를 추가적으로 활용하고 싶을 때
- synchronized 보다 세밀한 제어가 필요할 때 (예: 여러 개의 락을 관리)
정리
- 모니터(Monitor)는 객체 수준에서 자동으로 관리되는 동기화 메커니즘이며, synchronized 키워드로 쉽게 구현 가능합니다.
- 뮤텍스(Mutex)는 락 기반 동기화 기법으로, 보다 정밀한 제어가 가능하지만 lock() 및 unlock()을 명시적으로 관리해야 합니다.
- 자바에서는 synchronized와 ReentrantLock을 활용하여 모니터와 뮤텍스를 모두 사용할 수 있습니다.
- 간단한 동기화에는 모니터(synchronized), 더 정교한 동기화에는 뮤텍스(ReentrantLock)를 선택하는 것이 일반적입니다.
'🚣활동 > NHN Academy' 카테고리의 다른 글
Thread Pool (0) | 2025.02.04 |
---|---|
Deadlock (교착 상태) (0) | 2025.02.04 |
Main Thread, Single Thread, Multi Thread, Daemon Thread (1) | 2025.02.03 |
Thread (스레드) (0) | 2025.02.03 |
Process (프로세스) (0) | 2025.02.03 |