import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class MutexExample {
// 뮤텍스 객체 생성
private final Lock mutex = new ReentrantLock();
private int counter = 0;
// 임계 구역
public void increment() {
// 상호배제를 위해 뮤텍스를 잠금
mutex.lock();
try {
counter++;
System.out.println(Thread.currentThread().getName() + " incremented counter to " + counter);
} finally {
// 임계 구역을 벗어나면 뮤텍스 해제
mutex.unlock();
}
}
public static void main(String[] args) {
MutexExample example = new MutexExample();
// 여러 스레드 생성
Thread t1 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
example.increment();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "Thread-1");
Thread t2 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
example.increment();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "Thread-2");
// 스레드 시작
t1.start();
t2.start();
}
}
카운터가 0과 1 사이의 값을 가질 수 있는 세마포어
뮤텍스와 유사하게 동작하며 한 번에 하나의 스레드만 자원에 접근할 수 있도록 보장
바이너리 세마포어는 상호배제를 보장
각 스레드는 제한된 시간 내에 자원을 사용할 수 있는 기회를 가져야 함
자원이 사용되지 않을 때, 자원을 필요로 하는 스레드가 즉시 자원에 접근할 수 있도록 보장
import java.util.concurrent.Semaphore;
public class BinarySemaphoreExample {
// 바이너리 세마포어 객체 생성, 초기 카운터는 1
private final Semaphore binarySemaphore = new Semaphore(1);
public void accessResource() {
try {
binarySemaphore.acquire(); // wait()
System.out.println(Thread.currentThread().getName() + " is accessing the resource.");
Thread.sleep(1000); // 자원 사용 시간
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println(Thread.currentThread().getName() + " has finished using the resource.");
binarySemaphore.release(); // signal()
}
}
public static void main(String[] args) {
BinarySemaphoreExample example = new BinarySemaphoreExample();
// 여러 스레드 생성
Runnable task = () -> {
for (int i = 0; i < 5; i++) {
example.accessResource();
}
};
Thread t1 = new Thread(task, "Thread-1");
Thread t2 = new Thread(task, "Thread-2");
// 스레드 시작
t1.start();
t2.start();
}
}
카운팅 세마포어는 카운터가 0 이상인 값을 가질 수 있는 세마포어
여러 스레드가 동시에 자원에 접근할 수 있는 수를 제한
카운팅 세마포어는 카운터를 통해 특정 자원에 대한 동시 접근을 제어
각 스레드는 제한된 시간 내에 자원을 사용할 수 있는 기회를 가져야 함
interface Monitor {
void enter();
void exit();
}
class BankAccountMonitor implements Monitor {
private final Object lock = new Object();
private int balance = 0;
@Override
public void enter() {
synchronized (lock) {
// 임계 구역 진입
}
}
@Override
public void exit() {
synchronized (lock) {
// 임계 구역 퇴장
lock.notifyAll();
}
}
public void deposit(int amount) {
synchronized (lock) {
balance += amount;
System.out.println(Thread.currentThread().getName() + " deposited " + amount + ". Current balance: " + balance);
lock.notifyAll();
}
}
public void withdraw(int amount) {
synchronized (lock) {
while (balance < amount) {
try {
lock.wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
balance -= amount;
System.out.println(Thread.currentThread().getName() + " withdrew " + amount + ". Current balance: " + balance);
}
}
public static void main(String[] args) {
BankAccountMonitor account = new BankAccountMonitor();
Thread depositor = new Thread(() -> {
for (int i = 0; i < 5; i++) {
account.deposit(100);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "Depositor");
Thread withdrawer = new Thread(() -> {
for (int i = 0; i < 5; i++) {
account.withdraw(50);
try {
Thread.sleep(150);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "Withdrawer");
depositor.start();
withdrawer.start();
}
}
- 메커니즘
- 뮤텍스(Mutex) :
잠금 메커니즘(Locking Mechanism)
을 사용, 자원을 사용하려는 스레드가 뮤텍스를 잠그고, 사용을 마치면 뮤텍스를 해제- 바이너리 세마포어(Binary Semaphore) :
신호 메커니즘(Signal Mechanism)
을 사용, 세마포어를 획득하려는 스레드가 신호를 받고, 자원을 사용한 후 신호를 방출- 소유권
- 뮤텍스 : 뮤텍스를 잠근 스레드만이 뮤텍스를 해제할 수 있음
- 즉, 소유권이 있음
- 바이너리 세마포어 : 소유권이 없음, 세마포어를 획득한 스레드가 반드시 해제해야 하는 것은 아님(다른 스레드가 해제할 수 있음)
- 사용 용도
- 뮤텍스 : 주로 단일 프로세스 내에서 스레드 간의 상호 배제를 위해 사용
- 바이너리 세마포어 : 단일 프로세스뿐만 아니라 여러 프로세스 간의 동기화에도 사용될 수 있음