동시성 용어집
동시성 분야의 개념 31개를 한 장에 모았습니다. 갱신 손실 · 구조화된 동시성 · 낙관적 락 · 데드락 등 — 코드베이스에서 뽑아 뜻과 관계를 정리했습니다.
- 갱신 손실 (lost update)
- 두 요청이 같은 값을 읽고 각자 고쳐 쓰면 먼저 쓴 변경이 사라진다. 재고 · 잔액 · 쿠폰처럼 읽고-고치고-쓰는 흐름에서 생긴다
- 구조화된 동시성 (structured concurrency)
- 자식 작업의 수명이 그것을 연 스코프 안에 갇힌다는 원칙. 스코프는 자식이 모두 끝나야 끝나고, 하나가 실패하면 형제를 취소한다
- 낙관적 락 (optimistic locking, @Version)
- 잠그지 않고 읽은 뒤, 저장할 때 버전 컬럼이 그대로인지 비교한다. 다르면 예외를 내고 재시도한다. 충돌이 드문 갱신에 맞다
- 데드락 (deadlock, 교착 상태)
- 둘 이상의 실행 단위가 서로가 쥔 자원을 기다리며 영원히 멈춘 상태. 코프먼 네 조건이 모두 있을 때 생기고, 잠금 순서를 고정하면 순환 대기가 깨진다. DB 에서는 InnoDB 가 감지해 한쪽 트랜잭션을 롤백한다
- 동기화
- 여러 실행 단위가 같은 자원을 만질 때 순서와 배타를 보장한다. 잠금으로 막을지, 원자 연산으로 잠금 없이 갈지, 아예 공유하지 않을지 고른다
- 동시 갱신 제어
- 같은 행을 여러 요청이 동시에 고칠 때 한쪽 변경이 사라지지 않게 한다. 충돌이 드물면 낙관적 락, 잦거나 실패 비용이 크면 비관적 락, 단순 증감이면 조건부 원자 갱신을 고른다
- 동시성 (concurrency, 동시성 프로그래밍)
- 한 프로세스 안에서 여러 일을 겹쳐 진행하는 기법. 무엇으로 실행할지, 메모리를 어떻게 공유하고 지킬지, 기다림(IO)을 어떻게 다룰지를 차례로 정한다
- 락 경합 (lock contention)
- 여러 실행 단위가 같은 잠금을 잡으려 몰려 대부분이 기다리는 데 시간을 쓰는 상태. 코어를 늘려도 처리량이 오르지 않는다
- 레이스 컨디션 (race condition, 경쟁 상태, data race)
- 여러 실행 단위가 공유 자원을 동시에 읽고 쓰는 순서에 따라 결과가 달라지는 상태. 확인 후 실행(check-then-act)과 읽고-고치고-쓰기(read-modify-write)가 대표 모양이다
- 리액티브 스트림 (Reactive Streams, Reactor, Mono, Flux)
- 비동기 데이터 흐름을 Publisher·Subscriber·Subscription·Processor 로 잇고, 구독자가 request(n) 으로 받을 양을 알려 배압을 거는 논블로킹 표준. Reactor 의 Mono·Flux 가 구현이다
- 메모리 가시성 (memory visibility)
- 한 스레드가 쓴 값이 다른 스레드에 언제 보이는지의 문제. CPU 캐시·재배치 때문에 happens-before 관계가 없으면 보장되지 않는다
- 뮤텍스 (mutex, mutual exclusion, lock)
- 한 번에 하나만 임계 구역에 들어가게 하는 잠금. 잠근 쪽만 풀 수 있다. 여러 잠금을 순서 없이 잡으면 교착이 생긴다
- 분산락 (distributed lock)
- 여러 프로세스·노드가 공유 자원에 동시에 들어가지 않게 외부 저장소(Redis·ZooKeeper 등)에 잠금을 둔다. TTL 과 펜싱 토큰이 없으면 만료 뒤 두 쥔 자가 생긴다
- 블로킹 · 논블로킹 (blocking, non-blocking)
- 호출이 결과가 나올 때까지 실행 단위를 붙잡으면 블로킹, 곧바로 돌아오고 결과는 나중에 받으면 논블로킹이다. 호출한 쪽이 제어권을 돌려받느냐의 축이다
- 비관적 락 (pessimistic locking, PESSIMISTIC_WRITE)
- 읽는 시점에 행을 잠가(SELECT ... FOR UPDATE) 다른 트랜잭션의 수정을 커밋까지 막는다. 충돌이 잦거나 한 번의 실패가 비싼 차감에 맞다
- 비동기 처리
- IO 를 기다리는 동안 실행 단위를 막지 않고 다른 일을 하게 한다. 결과를 콜백으로 받을지, 조합 가능한 Future 로 받을지, 순차 코드 모양(async/await)으로 받을지 고른다
- 세마포어 (semaphore, counting semaphore)
- 허가(permit) N 개를 두고 동시에 들어갈 수 있는 실행 단위 수를 N 으로 제한한다. 외부 API 동시 호출 상한처럼 자원 수를 맞출 때 쓴다
- 스레드 (thread, OS thread, 플랫폼 스레드, platform thread)
- 프로세스 안에서 독립적으로 실행되는 OS 스케줄링 단위. 메모리는 프로세스와 공유하고 스택은 따로 가진다. 하나당 스택 메모리와 컨텍스트 스위칭 비용이 든다
- 스레드 풀 (thread pool, executor, ThreadPoolExecutor, ExecutorService)
- 스레드를 미리 정해진 수만큼 만들어 두고 작업 큐에서 꺼내 재사용한다. 생성 비용을 없애고 동시 실행 수에 상한을 건다. ThreadPoolExecutor 의 core·max·keepAlive·큐·거절 정책이 동작을 정한다
- 실행 단위 구성
- 일을 무엇 위에서 돌릴지 정한다 — OS 스레드를 직접 쓸지, 풀로 재사용할지, 가상 스레드나 코루틴처럼 가벼운 단위를 얹을지
- 원자 연산 (atomic operation, AtomicLong, AtomicReference, atomic)
- 읽기·계산·쓰기를 중간에 끼어들 수 없는 한 동작으로 한다. 카운터 증가·참조 교체처럼 작은 공유 값은 잠금 없이 AtomicXxx 로 지킨다
- 이벤트 루프 블로킹 (blocking the event loop, BlockHound)
- 소수의 이벤트 루프 스레드에서 JDBC·파일 IO·Thread.sleep·block() 같은 블로킹 호출을 해 그 스레드가 맡은 모든 연결이 함께 멈추는 상태
- 임계 구역 (critical section)
- 공유 자원을 만지는 코드 구간. 동시에 하나(또는 정해진 수)만 들어가야 결과가 맞는다. 짧게 잡을수록 경합이 준다
- 자원 고갈 (resource exhaustion, 스레드 고갈, pool exhaustion)
- 요청마다 스레드·커넥션을 새로 잡다가 메모리나 한도가 바닥나 새 작업을 받지 못하는 상태. 풀이면 모든 워커가 막혀 대기열만 느는 모양으로 나타난다
- 잠금 순서 고정 (lock ordering)
- 여러 행을 잠글 때 항상 같은 순서(예: id 오름차순)로 잠근다. 서로 반대 순서로 잠가 생기는 교착을 구조적으로 없앤다
- 처리량 (throughput, TPS)
- 단위 시간에 끝낸 작업 수. 동시성을 늘리는 목적이고, 경합이 커지면 오히려 줄어든다
- 컨텍스트 스위칭 (context switch)
- CPU 가 실행 중인 스레드의 레지스터·스택 상태를 저장하고 다른 스레드 상태로 바꾸는 일. 수 마이크로초가 들고 캐시가 식는다. 스레드가 많을수록 이 비용이 커진다
- 코루틴 (coroutine)
- 중단 지점에서 스스로 멈췄다가 이어서 실행되는 경량 실행 단위. 멈춘 동안 스레드를 놓아 주므로 적은 스레드로 많은 IO 대기를 겹칠 수 있다
- async/await (async await, Deferred, launch/async)
- 비동기 작업을 시작해 두고(async) 결과가 필요한 자리에서 기다리는(await) 방식. 콜백 없이 순차 코드 모양으로 여러 IO 를 겹친다
- CAS (compare-and-swap, compareAndSet, CAS, compare-and-exchange)
- 메모리 값이 기대값과 같을 때만 새 값으로 바꾸는 CPU 원자 명령. 실패하면 다시 읽고 재시도한다. 락-프리 자료구조와 원자 변수가 이 위에 선다
- SKIP LOCKED 작업 나눠 갖기 (FOR UPDATE SKIP LOCKED, NOWAIT)
- FOR UPDATE SKIP LOCKED 로 이미 잠긴 행은 건너뛰고 나머지만 가져간다. 여러 워커가 같은 테이블을 큐처럼 나눠 처리할 때 서로 기다리지 않는다
다른 분류