[SB 3기] 코드잇 스프린트 위클리페이퍼 15주차

JHLee·2025년 8월 17일
post-thumbnail

Q1. 멀티스레드 환경에서 발생하는 대표적인 문제 중 하나인 경쟁 상태(Race Condition)에 대해 설명하고, 이를 해결하기 위한 다양한 전략을 설명해보세요.


✅ 경쟁 상태(Race Condition)란?

  • 여러 스레드나 프로세스가 동시에 공유 자원(변수, 메모리, 파일 등)에 접근할 때, 실행 순서에 따라 결과가 달라질 수 있는 상태를 말한다.

👉 예시 :

  • A의 계좌에 15만원이 있을 때, 두 명이 동시에 1만원씩 입금하면 정상 결과는 17만원이어야 한다.
  • 하지만 두 스레드가 동시에 15만원을 읽어 각각 1만원을 더해 쓰면 최종 결과가 16만원으로 잘못 저장될 수 있다.

✅ 임계 구역(Critical Section)이란?

  • 공유 자원에 접근하는 코드 블록을 말한다.
  • 경쟁 상태가 발생할 수 있으므로 공유 자원의 독점을 보장해줘야 하는 영역이다.

📌 경쟁 상태 해결 조건 (3가지)

1. 상호 배제(Mutual Exclusion) : 한 스레드가 임계 구역을 실행 중이면 다른 스레드는 진입할 수 없어야 한다.

2. 진행(Progress) : 임계 구역에 실행 중인 스레드가 없을 때는, 대기 중인 스레드 중 하나가 반드시 진입할 수 있어야 한다.

3. 한정 대기(Bounded Waiting) : 특정 스레드가 무한정 대기하지 않도록 보장해야 한다. (기아 상태 방지)


🛠️ 경쟁 상태 해결 전략

  • 해결 방법은 크게 1) 락 기반 동기화, 2) 원자적 연산 활용, 3) Thread-safe 자료구조 사용으로 나눌 수 있다.

⭐️ 1. 동기화 전략 (Lock 기반)

  • 락을 걸어 순서를 보장하는 전통적인 방식

  • 스레드가 임계 구역에 순차적으로 진입하도록 제어한다.

  • 대표적인 해결 방법:

    • 뮤텍스(Mutex)
      • 한 번에 하나의 스레드만 공유 자원에 접근 가능
      • 다른 스레드는 해당 자원이 해제될 때까지 대기해야 함
      • 단순하고 안정적이지만, 동시에 많은 스레드가 몰리면 성능 저하 발생
    • 세마포어(Semaphore)
      • 동시에 접근할 수 있는 스레드의 최대 개수를 제한
      • ex. 최대 3개 스레드까지만 DB 접근 허용
      • 뮤텍스는 세마포어의 개수가 1인 특수한 경우라고 볼 수 있음
    • 모니터(Monitor)
      • 공유 자원과 접근 메서드를 객체 단위로 캡슐화하여 관리
      • 자바의 synchronized 블록이나 메서드가 대표적인 구현체

2. 원자적 연산 사용 (Lock-free)

  • 락 대신 CPU 명령어로 원자성을 보장하는 방식
  • CAS(Compare-And-Swap) 기반으로, 공유 자원 수정이 쪼갤 수 없는 단일 연산처럼 처리된다.
  • 다른 스레드가 끼어들 수 없다.
  • ex. AtomicInteger.incrementAndGet()

3. Thread-Safe 자료 구조 사용

  • 동기화가 내장된 자료구조 활용하는 방식
  • 멀티스레드 환경에서도 안전하게 데이터에 접근 가능하다.
  • ex. ConcurrentHashMap, CopyOnWriteArrayList, ConcurrentLinkedQueue

💡 상황에 따라 성능과 안정성을 고려해 적절한 방법을 선택해야 한다.

  • 락 기반 방식은 직관적이지만 성능 저하가 있을 수 있다.
  • 원자적 연산은 빠르지만 복잡한 로직에는 한계가 있다.
  • 복잡한 데이터 구조는 Thread-safe 자료구조를 사용하는 것이 더 안전하다.

Q2. 비동기 환경에서 MDC(Logback Mapped Diagnostic Context)나 SecurityContext 같은 컨텍스트 정보를 스레드 간에 전달해야 할 경우, 처리하는 방법에 대해 설명하세요.


✅ 비동기(Async) 환경이란?

  • 여러 작업들이 서로 영향을 주지 않고 독립적으로 실행될 수 있는 환경이다.
  • 요청 처리 스레드와 별도 스레드풀에서 작업을 실행함
  • 이는 응답 시간을 줄여 시간이 오래 걸리는 작업을 처리할 때 효율적이다.

⚠️ 발생할 수 있는 문제

  • ThreadLocal 기반 컨텍스트(MDC, SecurityContextHolder)는 스레드가 바뀌면 자동 전파되지 않는다. (사라짐)
  • ex. 로그(traceId)가 유실될 수 있음
  • ex. 인증/인가 정보가 사라져 권한체크가 실패할 수 있음

ThreadLocal은 스레드의 로컬 컨텍스트 변수로 스레드가 살아있는 동안 계속해서 유지되는 변수를 말한다. (스레드의 글로벌 변수)


🛠️ 문제 해결 방법 : TaskDecorator 활용

  • TaskDecorator는 스레드풀에서 실행되는 Runnable을 감싸 전·후 처리를 할 수 있게 해주는 인터페이스이다.
  • 이를 이용하면 비동기 실행 시에도 MDC와 SecurityContext같은 ThreadLocal 기반 컨텍스트를 안전하게 전파할 수 있다.
  • 원리 : 컨텍스트를 캡처 → 비동기 태스크에 래핑 → 실행 스레드에서 복원 → 실행 후 정리

1. TaskDecorator 구현

public class ContextCopyingTaskDecorator implements TaskDecorator {
    @Override
    public Runnable decorate(Runnable task) {
        // 1) Capture
        Map<String, String> callerMdc = MDC.getCopyOfContextMap();
        SecurityContext callerSec = SecurityContextHolder.getContext();

        return () -> {
            try {
                // 2) Restore
                if (callerMdc != null) MDC.setContextMap(callerMdc);
                SecurityContextHolder.setContext(callerSec);

                // 3) Run
                task.run();
            } finally {
                // 4) Clear
                MDC.clear();
                SecurityContextHolder.clearContext();
            }
        };
    }
}

2. 스레드풀에 등록 + @Async 메서드에서 활용

@EnableAsync
@Configuration
public class AsyncConfig {

    @Bean(name = "appExecutor")
    public ThreadPoolTaskExecutor appExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5);
        executor.setTaskDecorator(new ContextCopyingTaskDecorator());
        executor.initialize();
        return executor;
    }
}
@Service
public class MyService {
    @Async("appExecutor")
    public void doAsyncWork() {
        // 여기서 MDC와 SecurityContext 정상 동작
    }
}

🔁 스레드 간 컨텍스트 전달 원칙

  • 캡처(Capture): 현재 스레드의 컨텍스트 저장
  • 래핑(Wrap): 비동기 태스크를 컨텍스트 포함 형태로 감싸기
  • 복원(Restore): 실행 스레드에서 컨텍스트 복원
  • 정리(Clear): 실행 후 반드시 초기화 (스레드풀 재사용 시 누수 방지)

📄 참고 문서

profile
개발자로 성장하기

2개의 댓글

comment-user-thumbnail
2025년 8월 18일

깔끔하고 정확하게 정리돼있어서 잘봤습니다!

답글 달기
comment-user-thumbnail
2025년 8월 18일

질문들에 대한 핵심 내용들을 잘 담고 있는 포스트인것 같습니다 🔥

답글 달기