자바에서의 동시성 문제란?
자바에서 동시성 문제는 여러 스레드가 동시에 공유 자원에 접근 할 때 발생할 수 있는 문제.
- Race Condition : 둘 이상의 접근 흐름이 동시에 자원에 같은 자원에 접근해서, 실행 순서에 따라 결과가 달라지는 문제
- Deadlock: 여러 스레드가 서로 자원을 기다리며 영원히 대기 상태에 빠지는 문제
동기화(Synchronization)란?
여러 프로세스나 스레드가 공유자원에 동시에 접근하는 것을 제어하여, 데이터의 일관성과 무결성을 보장하는 기법
동시성을 제어하기 위해 뮤텍스, 세마포어 , BlockingQueue 가 있다.
세마포어의 목적과 동시성 제어의 방법 차이점을 알아보자.
뮤텍스나 세마포어는 동시성 제어를 위한 자료 구조이다.
동시성 제어를 위한 자료구조
뮤텍스(Mutex)
하나의 스레드만이 공유자원에 접근할 수 있도록 하여 race condition 상태를 방지하는 기법.
공유 자원을 점유하는 Thread 가 lock 을 걸면, 다른 thread 는 unlock 상태가 될 때까지 해당 자원에 접근 할 수 없다.
자바에서는 synchronized 키워드로 제어를 할 수 있다 : https://newfangled.tistory.com/63
세마포어(Semaphore)
Semaphore는 일정 수(S)의 스레드만이 동시에 공유 자원에 접근할 수 있도록 제어하는 동기화 기법.
세마포어 값은 사용 가능한 자원의 수로 초기화되며, 접근 시 S--, 자원 반납 시 S++ 연산을 수행합니다. 세마포어 값이 0이면 모든 자원이 사용 중임을 의미하며, 이후 접근하려는 스레드는 블록됩니다.
뮤텍스와 세마포어의 차이
mutex는 오직 1개의 process/thread만이 공유 자원에 접근할 수 있고, semaphore는 세마포어 변수의 값만큼의 process/thread들이 동시에 자원에 접근할 수 있습니다. mutex는 binary semaphore라고 할 수 있습니다.
동시성 처리를 하는 여러 방법 중 하나이다.
세마포어는 동시에 접근할 수 있도록 진입을 허용하는 스레드의 수를 n 개로 해주는 락이다.
BlockingQueue
동시성 처리에 또 다른 방법은 큐를 이용해 내부 자원에 가용한 대로 처리를 해주는 방법도 있다.
BlockingQueue vs Semaphore: 동시성 제어 도구의 비교와 선택 기준
공통점 요약
스레드 안전한 자료구조 | 스레드 동기화를 위한 도구 |
스레드 간 작업 전달 (Producer → Consumer) | 스레드 간 접근 수 제어 (자원 관리) |
내부적으로 락/조건변수 등을 사용해 동시성 제어 | 내부적으로 카운터를 사용해 허용량 제어 |
주요 차이점 요약
목적 | 작업(데이터)을 안전하게 전달하고 분산 처리 | 제한된 자원에 접근하는 스레드 수를 제어 |
동기/비동기 흐름 | 비동기: 큐에 넣고 바로 다음 작업 가능 | 동기: 자원 없으면 직접 대기 (blocking) |
FIFO 보장 | O (큐 구조이므로 순서 보장) | X (대기 순서 보장 안 됨, JVM 스케줄링에 의존) |
스레드 간 역할 구분 | 생산자 / 소비자 스레드 구분 | 동일한 역할의 스레드들이 자원을 놓고 경쟁 |
어떤 상황에 어떤 걸 써야 할까?
BlockingQueue를 사용하는 게 더 적합한 경우:
- 생산자/소비자 패턴이 필요한 경우
- 로그 처리, 작업 분산 처리, 비동기 이벤트 큐
- 예: 사용자가 보낸 요청을 받아 작업 큐에 넣고, 워커 스레드가 하나씩 꺼내 처리
- 작업 순서를 보장하고 싶을 때
- FIFO로 순서 보장됨
- 처리를 위임하고, 주 스레드는 즉시 반환되길 원할 때
- 요청 처리 후 응답은 비동기적으로 나중에 돌아와도 될 때
- 작업량이 많아 멀티스레드 처리를 하고 싶을 때
- 병렬 처리를 자연스럽게 분산시켜줌
Semaphore를 사용하는 게 더 적합한 경우:
- 동시에 접근 가능한 자원의 개수를 제한하고 싶을 때
- DB 커넥션 풀, API 호출 수 제한, 임계영역 보호
- 스레드가 직접 자원을 획득하고 처리해야 할 때
- 중간에 전달하지 않고, 스레드가 끝까지 책임지고 처리할 경우
- 우선 순위나 순서가 상관 없고, 오직 '몇 개까지' 허용할지만 중요할 때
- FIFO 순서 보장 필요 없는 경우
락을 획득했을 때 FIFO 보장 여부
- BlockingQueue는 FIFO 큐이므로, 작업 순서를 명시적으로 보장해줘야 할 때 탁월함.
- Semaphore는 FIFO 보장을 하지 않는다.
JVM이 어떤 스레드에게 permit을 줄지는 예측 불가함 (비결정적).
결론: 동기 vs 비동기의 흐름 차이가 가장 큰 결정 포인트인가?
. 동기/비동기 흐름의 차이가 실제로 가장 핵심적인 구분 포인트.
동기 (Semaphore) | 호출한 스레드가 자원을 획득할 때까지 직접 기다림 |
비동기 (BlockingQueue) | 스레드는 큐에 넣고 나면 바로 다음 일 수행 가능 |
정리 문장
Semaphore는 스레드 간 자원 접근 수 제어를 위한 동기 제어 도구이고,
BlockingQueue는 작업을 안전하게 전달하기 위한 비동기 작업 큐입니다.순서를 보장해야 하거나 생산자-소비자 패턴이 필요하면 BlockingQueue, (비동기)
자원 개수 제어나 임계영역 보호에는 Semaphore (동기)가 적합합니다.
'JAVA' 카테고리의 다른 글
Java 예외 처리, 제대로 알고 쓰자 (1) | 2025.03.26 |
---|---|
바이트코드 조작 (0) | 2025.03.24 |
클래스 로더란? (0) | 2025.03.24 |
JVM, JDK, JRE 의 차이, JVM의 동작방식 (0) | 2025.03.23 |
네트워크 - 프로그램1 (1) | 2025.01.29 |