💡 동기화(Synchronization) 기본 개념
동기화란 여러 스레드(또는 프로세스)가 동시에 공유 자원에 접근할 때 데이터 충돌이나 레이스 컨디션을 막기 위해 자원 접근을 통제하는 것을 의미한다.
왜 필요한가?
→ 여러 스레드가 같은 변수나 메모리 공간을 수정할 때 충돌이 발생하면, 프로그램이 예측 불가능 하게 동작할 수 있다.
→ 그래서 "한 번에 하나만" 자원을 접근하도록 제어하는 기술이 필요하다.
🔄 동기화 주요 기법: 뮤텍스 vs 세마포어
1. 뮤텍스(Mutex)
- Mutual Exclusion (상호 배제)에서 온 말
- 한 번에 하나의 스레드만 자원에 접근할 수 있게 함
- Lock을 걸고, 작업이 끝나면 Unlock한다
- 락을 걸었던 스레드만 해제할 수 있다 (권한 있음)
자바에서는
- synchronized 키워드
- ReentrantLock 클래스를 통해 구현할 수 있다.
2. 세마포어(Semaphore)
- 신호등 개념
- 여러 개(N개)의 스레드가 동시에 자원 접근 가능하게 허용
- 세마포어는 내부적으로 카운터를 가지고 있다.
- acquire() → 카운터 -1 (자원 사용)
- release() → 카운터 +1 (자원 반납)
- 누구나 release()를 호출할 수 있다 (락 건 스레드가 아니어도)
자바에서는
- Semaphore 클래스를 사용한다.
🆚 뮤텍스와 세마포어 차이 정리
구분 | 뮤텍스 | 세마포어 |
보호 자원 수 | 1개 | 여러개 가능 |
동작 방식 | lock() - unlock() | acquire() - release() |
해제 권한 | 락 건 스레드만 해제 | 누구나 release 가능 |
사용 목적 | 하나의 공유 자원 보호 | 제한된 수량 자원 관리 (ex. 주차장 자리 등) |
🍽 실생활 비유
- 뮤텍스 = 화장실 한 칸
→ 한 사람만 사용 가능, 문 잠가놓고, 끝나면 열어줌. - 세마포어 = 주차장 세 자리
→ 최대 3대까지 주차 가능, 하나 나가면 다른 차 들어올 수 있음.
☕️ 자바 코드 예제
뮤텍스 (ReentrantLock 사용)
package os;
import java.util.concurrent.locks.ReentrantLock;
public class MutexExample {
private static int count = 0; // 공유 자원 (모든 스레드가 접근하려는 변수)
private static ReentrantLock lock = new ReentrantLock(); // 뮤텍스 역살을 하는 Lock 객체 생성
public static void main(String[] args) {
// 각 스레드가 실행할 작업 정의
Runnable task = () -> {
lock.lock(); // 공유 자원(count) 접근 전에 락을 건다
try {
for (int i = 0; i < 1000; i++) {
count++; // count 값을 안전하게 1씩 증가시킨다
}
} finally {
lock.unlock(); // 작업이 끝났으면 락을 반드시 해제한다
}
};
// 두 개의 스레드 생성
Thread t1 = new Thread(task);
Thread t2 = new Thread(task);
t1.start(); // 첫 번째 스레드 시작
t2.start(); // 두 번째 스레드 시작
try {
t1.join(); // t1 스레드가 끝날 때까지 기다림
t2.join(); // t2 스레드가 끝날 때까지 기다림
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("최종 count: " + count); // 기대 출력: 2000 (1000 + 1000)
}
}
세마포어 (Semaphore 사용)
import java.util.concurrent.Semaphore;
public class SemaphoreExample {
static Semaphore parkingLot = new Semaphore(3); // 주차 공간 3개
public static void main(String[] args) {
for (int i = 1; i <= 5; i++) {
final int carNum = i;
new Thread(() -> {
try {
System.out.println("🚗 차량 " + carNum + " 주차 대기 중");
parkingLot.acquire();
System.out.println("✅ 차량 " + carNum + " 주차 성공");
Thread.sleep(2000); // 주차 중
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("🏁 차량 " + carNum + " 주차 끝, 출차");
parkingLot.release();
}
}).start();
}
}
}
📌 최종 정리
- 동기화는 멀티스레드 환경의 필수 요소다.
- 뮤텍스는 오직 하나의 자원을 보호할 때 사용한다.
- 세마포어는 여러 개의 자원을 제한적으로 사용할 때 사용한다.
- 자바에서는 ReentrantLock과 semaphore로 쉽게 구현할 수 있다.
🙏 회고 포인트
- "왜 동기화가 필요한가?"를 상황 예시와 함께 이해하는 게 중요하다.
- 단순히 코드 암기 X -> 진짜 적용할 상황을 머릿속에 그려보는 것이 더 중요하다.
- 실무에서는 DB 커넥션 풀, 멀티 스레드 캐시 관리, API 요청 제한 등에 세마포어가 쓰인다.
📚 전체 예제 및 추가 자료는 GitHub - daily-cs-study에서 확인 가능합니다. 함께 성장해요!
'컴퓨터 과학 > 운영체제' 카테고리의 다른 글
💻 [운영체제] 프로세스, 스레드, 멀티태스킹, 동기화까지 운영체제 핵심 개념 정리 (0) | 2025.04.26 |
---|---|
💻 [운영체제] 가상 메모리와 스와핑 - RAM이 부족하면 왜 느려질까? (0) | 2025.04.25 |
💻 [운영체제] 메모리 관리 (페이징 vs 세그멘테이션) (0) | 2025.04.25 |
💻 [운영체제] CPU 스케줄링 알고리즘 정리 (FCFS, SJF, Priority, RR) (0) | 2025.04.24 |
💻 [운영체제] 멀티태스킹 & 컨텍스트 스위칭 정리 (0) | 2025.04.24 |