Concurrency 면접 질문
동시성 인터뷰 질문은 다중 스레드 시스템을 설계하고 문제를 해결하는 능력을 평가합니다. 시니어 엔지니어와 플랫폼 팀은 종종 스레드 안전성, 락 전략 및 성능 영향에 대해 심층 질문을 받습니다. 이러한 질문을 마스터하여 깊은 시스템 수준 사고를 보여주세요.
Concurrency 면접에서 다루는 내용
스레드 안전성 및 원자성
락, 원자 연산 또는 불변 데이터를 사용하여 공유 상태가 동시 접근 하에서 올바르게 접근되도록 보장.
교착 상태 및 라이브락
락 순서 및 타임아웃 메커니즘을 포함하여 스레드가 무기한 차단되는 조건을 인식하고 방지.
메모리 모델 및 가시성
happens-before 관계, volatile 및 CPU 캐시가 다중 스레드 정확성에 미치는 영향 이해.
동시성 패턴
생산자-소비자, 읽기-쓰기 락, 스레드 풀, 액터 모델 구현.
샘플 Concurrency 면접 질문
- 프로세스와 스레드의 차이점을 설명하세요. 스레딩이 멀티프로세싱보다 나은 예를 들어보세요.좋은 답변이 다루는 것
- 프로세스는 독립된 메모리 공간을 가지며, 스레드는 프로세스 내에서 메모리를 공유함
- 스레드는 생성 및 컨텍스트 스위칭 비용이 낮음
- 스레드 간 통신이 프로세스 간 통신보다 효율적임
- 하나의 스레드가 블록되어도 다른 스레드는 실행 가능 (멀티코어 활용)
샘플 답변 보기
프로세스는 운영체제로부터 독립된 메모리 공간(코드, 데이터, 힙, 스택)을 할당받는 실행 단위입니다. 반면 스레드는 하나의 프로세스 내에서 실행되는 경량 실행 단위로, 프로세스의 메모리 공간을 공유합니다. 따라서 스레드는 생성 속도가 빠르고 컨텍스트 스위칭 오버헤드가 적습니다. 스레딩이 멀티프로세싱보다 나은 예로는 웹 서버가 있습니다. HTTP 요청마다 새로운 프로세스를 생성하면 메모리와 CPU 시간이 많이 소모되지만, 스레드 풀을 사용하면 고정된 수의 스레드로 많은 요청을 처리할 수 있습니다. 또한 스레드는 공유 메모리를 통해 빠르게 데이터를 교환할 수 있지만, 동기화 문제(예: 경쟁 조건)가 발생할 수 있습니다.
- 경쟁 조건이란 무엇인가요? Java/C++에서 포함된 간단한 코드 스니펫을 보여주고 락을 사용하여 수정하세요.좋은 답변이 다루는 것
- 경쟁 조건은 여러 스레드가 공유 데이터를 동시에 수정할 때 발생하는 문제
- 락(lock)을 사용하여 임계 구역을 보호함으로써 해결 가능
샘플 답변 보기
경쟁 조건(race condition)은 두 개 이상의 스레드가 공유 자원에 동시에 접근하여 예상치 못한 결과를 초래하는 현상입니다. 예를 들어, 계좌 잔액을 업데이트하는 코드에서 읽기-수정-쓰기 연산이 중단되지 않고 실행되지 않으면 잔액이 잘못 계산될 수 있습니다. 이를 해결하려면 뮤텍스와 같은 락을 사용하여 임계 구역을 보호해야 합니다. 아래 코드는 Java에서 경쟁 조건을 보여주고 synchronized 키워드로 수정한 예입니다.
- C++ 또는 Java에서 조건 변수를 사용하여 스레드 안전한 경계 큐(블로킹 큐)를 구현하세요.좋은 답변이 다루는 것
- 조건 변수는 특정 조건이 충족될 때까지 스레드를 대기시킴
- 블로킹 큐는 put()과 take()에서 각각 공간/데이터가 없으면 대기
샘플 답변 보기
조건 변수(condition variable)는 스레드가 특정 조건을 기다리게 하는 동기화 도구입니다. C++에서는 std::condition_variable을, Java에서는 Condition 인터페이스를 사용합니다. 블로킹 큐(blocking queue)는 스레드 안전한 큐로, 큐가 가득 차면 put()이 대기하고, 큐가 비면 take()가 대기합니다. 구현 시 락과 함께 조건 변수를 사용하여 효율적인 대기/알림을 구현합니다. 아래 코드는 C++로 구현한 예시입니다.
- 프로그래밍 방식으로 교착 상태를 어떻게 감지하겠습니까? 교착 상태 감지 알고리즘에 대한 의사 코드를 작성하세요.좋은 답변이 다루는 것
- 교착 상태는 두 개 이상의 스레드가 서로가 가진 자원을 기다리며 무한 대기하는 상태
- 감지는 자원 할당 그래프를 사용하거나 대기-죽임 알고리즘으로 가능
샘플 답변 보기
교착 상태(deadlock)는 각 스레드가 다른 스레드가 점유한 자원을 요청하면서 모두 블록된 상태입니다. 프로그래밍 방식으로 감지하려면 자원 할당 그래프(resource allocation graph)를 유지하며, 사이클이 존재하는지 주기적으로 검사합니다. 또는 각 스레드가 일정 시간 내에 락을 획득하지 못하면 교착 상태로 간주하는 타임아웃 기법을 사용할 수도 있습니다. 아래는 자원 할당 그래프를 사용한 교착 상태 감지 알고리즘의 의사 코드입니다.
- 락 프리 프로그래밍에서 ABA 문제란 무엇인가요? 구체적인 시나리오와 해결책을 제시하세요.좋은 답변이 다루는 것
- ABA 문제는 CAS 연산에서 값이 A에서 B로 변경되었다가 다시 A로 돌아올 때 발생
- 해결책은 태그 또는 버전 번호를 함께 저장하는 것
샘플 답변 보기
ABA 문제는 lock-free 프로그래밍에서 compare-and-swap (CAS) 연산을 사용할 때 발생합니다. 스레드가 메모리 위치의 값을 읽고 A임을 확인한 후 CAS를 수행하기 전에, 다른 스레드가 그 값을 A에서 B로 바꾸고 다시 A로 되돌리면 CAS가 성공하지만 실제로는 상태가 변경된 것입니다. 예를 들어, 연결 리스트에서 노드를 제거하고 재사용하는 경우에 발생할 수 있습니다. 해결책은 더블-워드 CAS 또는 태그(버전 번호)를 함께 저장하여 값을 읽을 때마다 태그도 증가시키는 방법입니다. Java에서는 AtomicStampedReference 클래스가 이를 지원합니다.
- robots.txt를 존중하고 스레드 풀을 사용하는 동시 웹 크롤러를 설계하세요. 주요 구성 요소를 보여주세요.좋은 답변이 다루는 것
- robots.txt를 존중하여 크롤링 정책 준수
- 스레드 풀로 동시성 제어 및 리소스 관리
샘플 답변 보기
동시 웹 크롤러는 여러 웹 페이지를 효율적으로 내려받기 위해 스레드 풀을 사용하고, robots.txt를 준수하여 사이트 부하를 조절합니다. 주요 구성 요소는 URL 큐(우선순위 있음), HTML 파서, robots.txt 파서, HTTP 다운로더, 스레드 풀, 저장소입니다. 스레드 풀은 고정된 수의 스레드로 작업을 실행합니다. 각 스레드는 URL 큐에서 URL을 가져와 robots.txt를 확인한 후 HTTP 요청을 보내고 응답을 파싱하여 새로운 URL을 큐에 추가합니다. robots.txt는 주기적으로 다운로드하여 캐싱합니다. 크롤링 속도는 도메인별 지연(delay) 설정으로 조절합니다.
- Java 메모리 모델을 설명하세요: 'volatile'이 제공하는 보장은 무엇인가요? 공유 플래그 예를 사용하세요.좋은 답변이 다루는 것
- Java 메모리 모델은 스레드 간 메모리 가시성과 재배치 제어를 정의
- volatile은 가시성과 순서 보장 (happens-before) 을 제공
샘플 답변 보기
Java 메모리 모델(JMM)은 스레드가 변수를 읽고 쓸 때의 동작을 규정합니다. volatile 키워드는 여러 스레드가 공유 변수를 볼 때 가시성을 보장합니다. 즉, volatile 변수에 대한 쓰기는 다른 스레드가 즉시 볼 수 있으며, 컴파일러가 변수를 캐싱하지 않도록 합니다. 또한 volatile 변수 앞뒤의 읽기/쓰기는 재배치되지 않습니다(happens-before 규칙). 예를 들어, boolean flag를 volatile로 선언하면 한 스레드가 flag를 true로 설정한 후 다른 스레드가 그 값을 읽을 때 항상 최신 값을 보장합니다. 하지만 volatile은 원자성을 보장하지 않으므로 복합 연산에는 락이 필요합니다.
- 높은 경합 카운터가 있다면 뮤텍스, 원자 연산 또는 락 프리 접근 방식을 사용하겠습니까? 성능과 트레이드오프를 비교하세요.좋은 답변이 다루는 것
- 뮤텍스는 단순하지만 오버헤드가 크고 컨텍스트 스위칭 유발
- 원자 연산은 가벼우나 CAS 실패 시 재시도 필요
- 락 프리는 복잡하지만 확장성 높음
샘플 답변 보기
높은 경합(high contention) 카운터의 경우, 뮤텍스는 구현이 간단하지만 스레드가 대기할 때 컨텍스트 스위칭이 발생하여 성능이 저하됩니다. 원자 연산(atomic operations, 예: AtomicInteger)은 CAS(compare-and-swap)를 사용하여 락 없이 업데이트하므로 경합이 높을 때 CAS 실패가 많아져 재시도 비용이 증가할 수 있습니다. 락 프리 접근 방식(예: fetch-and-add)은 하드웨어 지원을 받아 더 효율적일 수 있지만 구현이 복잡합니다. 일반적으로 경합이 매우 높은 상황에서는 뮤텍스보다는 원자 연산이 선호되며, 특수한 경우 락 프리도 고려됩니다. 하지만 확장성과 예측 가능성을 위해 분산 카운터 또는 배칭 기법을 사용하기도 합니다.
준비 방법
- 고전적인 동시성 프리미티브(뮤텍스, 세마포어, 조건 변수)를 처음부터 구현하는 연습을 하세요.
- 실제 버그를 연구하세요: 교착 상태, 라이브락, 우선순위 역전. 스레드 덤프로 진단하는 방법을 알아야 합니다.
- 동시성과 병렬성의 차이점과 각각을 사용할 시점을 이해하세요.
- 락 프리 기술을 배우세요: CAS, 트랜잭셔널 메모리, 해저드 포인터.
- 경합 시 성능 메트릭(처리량 대 지연 시간)에 대해 논의할 준비를 하세요.
자주 묻는 질문
Java/C++의 모든 동시성 API를 알아야 하나요?
핵심 패턴에 집중하세요: 락, 세마포어, 스레드 풀, 원자 연산. 깊이가 폭보다 중요합니다.
동시성 설계 질문을 어떻게 준비하나요?
스레드 풀, 속도 제한기, 동시 해시 테이블과 같은 시스템 설계를 연습하세요. 트레이드오프를 개략적으로 설명하세요.
가장 까다로운 동시성 개념은 무엇인가요?
메모리 모델의 특이점, 예를 들어 적절한 동기화 없이 재정렬 및 가시성 문제.
화이트보드에 코드를 작성하라는 요청을 받을까요?
네, 스레드 안전한 데이터 구조를 구현하거나 동시성 버그를 수동으로 수정할 것으로 예상하세요.
락 프리 프로그래밍이 얼마나 중요한가요?
고급 주제이지만 깊은 이해를 보여줍니다. 기본 CAS 및 ABA 솔루션을 알아두세요.
즉각적인 AI 피드백으로 Concurrency 질문 연습하기
이력서를 업로드하고 맞춤형 모의 면접을 받아 무엇을 개선해야 할지 정확히 확인하세요 — 무료로 시작하세요.