Important Study - Mutex & Semaphore
뮤택스 ( Mutex ) 와 세마포어 ( Semaphore ) 는 왜 중요할까?
-
동시성 프로그래밍의 가장 큰 숙제는 공유자원 관리일 것이다.
-
공유자원을 안전하게 관리하기 위해서는 상호배제(Mutual exclusion)를 달성하는 기법이 필요하다.
-
뮤텍스와 세마포어는 이를 위해 고안된 기법으로 서로 다른 방식으로 상호배제를 달성한다.
뮤택스 ( Mutex ) 란 ?
-
동기화 대상이 하나일 때 사용하며, Key를 기반으로 한다.
-
Key를 가진 프로세스, 스레드만이 접근 가능하고 완료 후 다음 스레드에게 key를 넘기기 전까지는 다른 스레드는 접근할 수 없다.
-
즉, 뮤텍스 개체는 두 스레드가 동시에 사용할 수 없다.
-
Lock을 가짐 ( 0, 1 상태 뿐이라 )
-
자원을 소유하고 책임을 가진다.
-
소유하고 있는 스레드만이 이 뮤택스를 해제할 수 있다.
-
프로세스의 범위를 가지며, 프로세스가 종료될 때 자동으로 Clean up 된다.
-
예를들면 다음과 같다.
- A라는 사람이 키를 가지고 하나뿐인 화장실을 감. 다음 사람들은 줄을 서있음. A가 볼일을 다 보고, 와서 다음 사람에게 화장실 Key를 넘김. Key를 받은 사람이 화장실을 감.
-
뮤택스를 구현한 예시 코드이다.
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class MutexExample { private final Lock mutex = new ReentrantLock(); public void performCriticalSection() { try { // 뮤텍스 획득 mutex.lock(); // 크리티컬 섹션 (임계 영역) 코드 System.out.println("Critical Section: " + Thread.currentThread().getName()); // 뮤텍스 해제 } finally { mutex.unlock(); } } public static void main(String[] args) { MutexExample example = new MutexExample(); // 여러 스레드에서 크리티컬 섹션 접근 시도 for (int i = 0; i < 5; i++) { new Thread(() -> { example.performCriticalSection(); }).start(); } } }
-
이 코드에서 ReentrantLock 객체를 생성하고 performCriticalSection 메서드에서 크리티컬 섹션을 진입하기 전에 lock() 메서드를 호출하여 뮤텍스를 획득하고, 크리티컬 섹션을 빠져나갈 때에는 unlock() 메서드를 호출하여 뮤텍스를 해제한다.
-
이렇게 함으로써 한 번에 하나의 스레드만 크리티컬 섹션에 접근할 수 있다.
-
세마포어 ( Semaphore ) 란?
-
동기화 대상이 하나 이상일 때 사용한다.
-
정해진 갯수 만큼의 스레드가 접근할 수 있으며, 하나의 스레드가 접근 할 때마다 갯수의 수가 마이너스 되고, 나오면 플러스가 된다.
-
자원 소유가 불가능하다.
-
다른 스레드가 세마포어를 해제할 수 있다.
-
시스템 범위에 걸쳐있고, 파일 시스템 상의 파일로 존재한다.
-
예를들면 다음과 같다.
- 화장실이 3개가 있고, 한 사람이 들어가면 2개가 남는다. 그리고 그 사람이 나오면 다시 3개가 된다. 3명이 들어가서 화장실 남은 갯수가 0개면, 다음에 오는 사람들은 기다려야 한다.
-
세마포어를 구현한 예시 코드이다.
import java.util.concurrent.Semaphore; public class SemaphoreExample { private static final int MAX_AVAILABLE_RESOURCES = 3; private static final Semaphore semaphore = new Semaphore(MAX_AVAILABLE_RESOURCES, true); public void accessSharedResource() { try { // 세마포어 획득 semaphore.acquire(); // 공유 자원에 대한 작업 수행 System.out.println("Accessing shared resource: " + Thread.currentThread().getName()); // 세마포어 반환 } catch (InterruptedException e) { e.printStackTrace(); } finally { semaphore.release(); } } public static void main(String[] args) { SemaphoreExample example = new SemaphoreExample(); // 여러 스레드에서 공유 자원에 접근 시도 for (int i = 0; i < 5; i++) { new Thread(() -> { example.accessSharedResource(); }).start(); } } }
-
이 코드에서 Semaphore 객체를 생성하고 accessSharedResource 메서드에서 세마포어를 획득하기 위해 acquire() 메서드를 호출한다. 세마포어가 획득되면 공유 자원에 대한 작업을 수행하고, 마지막에 release() 메서드를 호출하여 세마포어를 반환한다.
-
이렇게 함으로써 지정된 수의 스레드만이 동시에 공유 자원에 접근할 수 있다.
-
뮤택스 vs 세마포어
Mutex vs Semaphore
속성 | Mutex | Semaphore |
---|---|---|
용도 | 단일 자원 동기화 | 다중 자원 동기화 |
동작 기반 | Key | Count |
동시 접근 제어 | 두 스레드 동시 접근 불가 | 정해진 갯수만큼 동시 접근 가능 |
자원 소유 및 책임 | 소유 및 책임이 있음 | 자원 소유 불가능 |
해제 권한 | 뮤텍스를 소유한 스레드만 해제 가능 | 다른 스레드가 해제 가능 |
범위 | 프로세스 범위 | 시스템 범위 |
Clean up | 자동으로 Clean up 됨 | 수동으로 해제 필요 (파일 등) |