CS Repository/운영체제 - Dinosaur Book + @

동시성 vs 병렬성

조금씩 차근차근 2026. 1. 6. 01:00

오랜만에 용어를 다시보니 헷갈려서 정리한다.

동시성 vs 병렬성

핵심 정의

  • 동시성(Concurrency): 여러 작업이 같은 시간 구간에 진행되며, 실행이 교차(interleave) 될 수 있는 성질.
    • 단일 코어에서도 가능(스케줄링/컨텍스트 스위치).
  • 병렬성(Parallelism): 여러 작업이 같은 시각에 실제로 실행되는 성질.
    • 멀티코어/멀티프로세서에서 물리적 동시 실행.

따라서, 병렬 실행은 “같은 시간 구간에 진행”을 만족하므로 동시성도 만족한다.

하지만 동시성은 단일 코어에서도 성립하므로 병렬성이 없어도 동시성은 성립한다.

이 관계를 집합으로 보면

Concurrency ⊇ Parallelism

 

상호 배타적이 아니라, 병렬성은 동시성 안에 포함된다(포함 관계).

예시로 구분

  • 동시성 O, 병렬성 X
    • 단일 코어에서 스레드 A/B가 번갈아 실행됨(타임슬라이스).
    • 실행이 섞이므로 race condition 가능.
  • 동시성 O, 병렬성 O
    • 2코어에서 스레드 A/B가 동시에 실행.
    • 역시 race condition 가능(오히려 더 빈번/재현 어려움).
  • 동시성 X, 병렬성 X
    • 작업이 완전히 순차적으로만 실행되고 겹치지 않음(단일 실행 흐름).
    • “병렬성 X, 동시성 O”가 가능하다는 점 때문에 두 개념이 배타적이 아니고, 병렬성이 동시성의 특수 케이스로 취급된다.

동시성 문제

동시성 문제(concurrency issue)는 여러 실행 흐름(스레드/프로세스/코루틴/요청)이 같은 자원(메모리, DB 레코드, 파일, 외부 상태 등)을 “겹치는 시간”에 접근할 때, 접근 순서(인터리빙)에 따라 결과가 달라지거나 데이터가 깨지는 현상을 말한다.

핵심은 “동시에 실행된다”기보다 서로의 실행이 중간에 끼어들 수 있다는 점이다.

1) Race Condition (경쟁 상태)

  • 결과가 누가 먼저 실행되느냐에 의존.
  • 예: count = count + 1 같은 연산은 내부적으로 “읽기→계산→쓰기”로 쪼개질 수 있어, 두 스레드가 섞이면 증가가 누락된다(“lost update”).

2) Atomicity Violation (원자성 위반)

  • “한 번에 끝나야 하는 작업”이 중간에 끊겨 다른 작업이 끼어들어 부분 상태가 노출됨.
  • 예: 잔액 차감 후 이체 내역 기록 전에 다른 요청이 잔액을 읽어 잘못된 판단을 함.

3) Visibility / Reordering 문제 (가시성/재정렬)

  • 한 스레드가 쓴 값이 다른 스레드에 즉시/항상 보이지 않거나, 컴파일러/CPU가 명령을 재정렬해 예상과 다른 순서로 관측될 수 있음.
  • Java에서는 volatile, synchronized, Lock, Atomic* 등이 “happens-before” 관계를 만들어 이를 제어한다.

4) Deadlock (교착 상태)

  • A는 락1을 잡고 락2를 기다리고, B는 락2를 잡고 락1을 기다리는 식으로 서로 영원히 대기.

5) Starvation / Livelock

  • 특정 작업이 계속 우선순위에서 밀려 진행을 못 하거나(starvation),
  • 서로 양보/재시도를 반복해 일은 안 끝나는 상황(livelock).

왜 생기나

  • 공유 자원에 대한 접근이 동기화 없이 이루어짐
  • “체크 후 실행(check-then-act)” 패턴이 분리되어 있음
    • 예: if (재고 > 0) 재고-- 는 두 요청이 동시에 통과할 수 있음
  • 분산 환경에서는 더 복잡: 여러 인스턴스가 같은 DB/캐시/락 없이 상태를 갱신하면 쉽게 발생

흔한 징후

  • 재현이 어렵고, 부하가 높을수록 빈도가 증가
  • 로그/모니터링상 “가끔” 데이터 불일치, 중복 처리, 음수 재고, 잔액 불일치 등이 나타남

실무에서 자주 쓰는 대응

  • 임계 구역 보호: synchronized/Lock/세마포어 등
  • 원자 연산 사용: AtomicInteger, CAS
  • 불변(immutable) 설계 및 공유 상태 최소화
  • DB에서 해결: 트랜잭션 격리수준, SELECT ... FOR UPDATE, 낙관적 락(version), 유니크 제약조건
  • 분산락/멱등성: Redis 락, 요청 키 기반 멱등 처리 등(필요한 경우)