JAVA

동시성 문제와 해결 방안

경딩 2025. 4. 9. 18:54

 

자바에서의 동시성 문제란?

자바에서 동시성 문제는 여러 스레드가 동시에 공유 자원에 접근 할 때 발생할 수 있는 문제.

  • 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: 동시성 제어 도구의 비교와 선택 기준

공통점 요약

BlockingQueueSemaphore
스레드 안전한 자료구조 스레드 동기화를 위한 도구
스레드 간 작업 전달 (Producer → Consumer) 스레드 간 접근 수 제어 (자원 관리)
내부적으로 락/조건변수 등을 사용해 동시성 제어 내부적으로 카운터를 사용해 허용량 제어

주요 차이점 요약

항목BlockingQueueSemaphore
목적 작업(데이터)을 안전하게 전달하고 분산 처리 제한된 자원에 접근하는 스레드 수를 제어
동기/비동기 흐름 비동기: 큐에 넣고 바로 다음 작업 가능 동기: 자원 없으면 직접 대기 (blocking)
FIFO 보장 O (큐 구조이므로 순서 보장) X (대기 순서 보장 안 됨, JVM 스케줄링에 의존)
스레드 간 역할 구분 생산자 / 소비자 스레드 구분 동일한 역할의 스레드들이 자원을 놓고 경쟁

어떤 상황에 어떤 걸 써야 할까?

 BlockingQueue를 사용하는 게 더 적합한 경우:

  1. 생산자/소비자 패턴이 필요한 경우
    • 로그 처리, 작업 분산 처리, 비동기 이벤트 큐
    • 예: 사용자가 보낸 요청을 받아 작업 큐에 넣고, 워커 스레드가 하나씩 꺼내 처리
  2. 작업 순서를 보장하고 싶을 때
    • FIFO로 순서 보장됨
  3. 처리를 위임하고, 주 스레드는 즉시 반환되길 원할 때
    • 요청 처리 후 응답은 비동기적으로 나중에 돌아와도 될 때
  4. 작업량이 많아 멀티스레드 처리를 하고 싶을 때
    • 병렬 처리를 자연스럽게 분산시켜줌

 Semaphore를 사용하는 게 더 적합한 경우:

  1. 동시에 접근 가능한 자원의 개수를 제한하고 싶을 때
    • DB 커넥션 풀, API 호출 수 제한, 임계영역 보호
  2. 스레드가 직접 자원을 획득하고 처리해야 할 때
    • 중간에 전달하지 않고, 스레드가 끝까지 책임지고 처리할 경우
  3. 우선 순위나 순서가 상관 없고, 오직 '몇 개까지' 허용할지만 중요할 때
    • FIFO 순서 보장 필요 없는 경우

 락을 획득했을 때 FIFO 보장 여부

  • BlockingQueue는 FIFO 큐이므로, 작업 순서를 명시적으로 보장해줘야 할 때 탁월함.
  • SemaphoreFIFO 보장을 하지 않는다.
    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