💻 프로그래밍

멀티스레딩

Multithreading

하나의 프로세스에서 여러 스레드를 실행하는 기법. CPU 활용률 향상. 동기화 이슈 주의 필요.

📖 상세 설명

멀티스레딩(Multithreading)은 하나의 프로세스 내에서 여러 스레드를 동시에 실행하는 프로그래밍 기법입니다. 각 스레드는 독립적인 실행 흐름을 가지면서 프로세스의 메모리와 자원을 공유합니다.

멀티스레딩의 장점은 CPU 활용률 향상, 응답성 개선, 자원 공유 효율성입니다. UI 스레드와 백그라운드 스레드를 분리하면 시간이 오래 걸리는 작업 중에도 UI가 멈추지 않습니다.

하지만 멀티스레딩은 동기화 문제를 수반합니다. 여러 스레드가 같은 데이터에 접근할 때 레이스 컨디션, 데드락, 라이브락 같은 문제가 발생할 수 있습니다. 뮤텍스, 세마포어, 모니터 같은 동기화 기법으로 이를 해결합니다.

Python은 GIL(Global Interpreter Lock) 때문에 CPU 바운드 작업에서 진정한 병렬 실행이 어렵습니다. Java, C++, Rust 등은 네이티브 멀티스레딩을 지원하며, Go는 고루틴으로 더 가벼운 동시성 모델을 제공합니다.

💻 코드 예제

Java
import java.util.concurrent.*;

public class MultithreadingExample {
    public static void main(String[] args) throws Exception {
        // 1. Thread 클래스 상속
        Thread thread1 = new Thread(() -> {
            System.out.println("스레드 1: " + Thread.currentThread().getName());
        });
        thread1.start();

        // 2. ExecutorService 사용 (권장)
        ExecutorService executor = Executors.newFixedThreadPool(4);

        for (int i = 0; i < 10; i++) {
            final int taskId = i;
            executor.submit(() -> {
                System.out.println("Task " + taskId + " 실행: " +
                    Thread.currentThread().getName());
            });
        }

        executor.shutdown();
        executor.awaitTermination(1, TimeUnit.MINUTES);

        // 3. 동기화가 필요한 경우
        Counter counter = new Counter();
        Thread t1 = new Thread(() -> { for(int i=0; i<1000; i++) counter.increment(); });
        Thread t2 = new Thread(() -> { for(int i=0; i<1000; i++) counter.increment(); });
        t1.start(); t2.start();
        t1.join(); t2.join();
        System.out.println("카운터: " + counter.getCount()); // 2000
    }
}

class Counter {
    private int count = 0;
    public synchronized void increment() { count++; }
    public int getCount() { return count; }
}

🗣️ 실무 대화 예시

백엔드 개발자
"이미지 처리 작업이 메인 스레드를 블록해서 API 응답이 느려져요."
시니어 개발자
"스레드 풀을 사용해서 이미지 처리를 백그라운드로 옮기세요. 작업 완료 후 콜백이나 이벤트로 알림을 보내면 됩니다."
백엔드 개발자
"풀 사이즈는 어떻게 정하죠? CPU 코어 수 기준으로요?"
면접관
"데드락이 발생하는 조건과 해결 방법을 설명해주세요."
지원자
"상호 배제, 점유 대기, 비선점, 순환 대기 네 조건이 모두 충족되면 데드락이 발생합니다. 락 획득 순서를 일관되게 하거나, 타임아웃을 설정하거나, tryLock으로 해결합니다."
면접관
"스레드 풀의 적절한 크기는 어떻게 결정하나요?"
지원자
"CPU 바운드 작업은 코어 수 정도, I/O 바운드 작업은 코어 수의 2배 이상이 일반적입니다. 실제로는 부하 테스트로 최적값을 찾아야 합니다."
리뷰어
"이 HashMap을 여러 스레드에서 접근하고 있는데, ConcurrentHashMap을 사용하거나 동기화를 추가해야 해요."
작성자
"ConcurrentHashMap으로 교체하겠습니다. 성능도 더 좋을 거예요."
리뷰어
"그리고 check-then-act 패턴도 있네요. 체크와 액션 사이에 다른 스레드가 끼어들 수 있어요."

⚠️ 주의사항

🔗 관련 용어

📚 더 배우기