Thread와 대안들

2025. 6. 2. 08:45·SW 공학 & 프로그래밍 언어/Java
728x90
반응형
  • Java의 Tread 클래스는 동시성 프로그래밍의 기초이자, 과거부터 지금가지 진화해 온 병렬처리의 기반 도구입니다.
  • 스레드와 동기화는 고성능 애플리케이션 개발에 필수적인 요소입니다.
  • 멀티스레드 환경에서 자원을 안전하게 공유하고 효율적으로 관리하기 위해 동기화는 필수적입니다.

1. Thread 클래스란?

  • java.lang.Thread
  • Java에서 독립 실행 흐름(스레드)를 만들기 위한 핵심 클래스
  • JVM 내부의 스레드 스케줄러에 의해 관리되며, OS의 실제 스레드에 매핑됩니다.

2. 주요 필드 및 핵심 기능

기능 설명
start() 새로운 스레드를 시작하여 run() 실행
run() 스레드에서 실행할 로직 정의 (Runnable 인터페이스 기반)
join() 해당 스레드가 끝날 때까지 대기
sleep(ms) 스레드를 일정 시간 정지시킴 (CPU 사용 없음)
yield() CPU를 다른 스레드에게 양보하려 시도(우선순위 힌트를 제공)
interrupt() 스레드에 인터럽트 신호 전달
isAlive() 스레드가 실행 중인지 확인
setDaemon(true) 데몬 스레드 지정 (JVM 종료 조건과 무관)

3. 현재 Thread의 실무 사용률과 문제점

항목 설명
직접 사용 빈도
매우 낮아짐 (대부분 Executors, CompletableFuture, ForkJoinPool, VirtualThread 사용)
단점 스레드 생성 비용 큼 (JVM ↔ OS 스레드 1:1)생성 수 제한 존재 (수천 개 수준)직접 제어 로직이 많아 복잡
특징 여전히 학습용, 데모용, 시스템 수준 코드에서 사용됨

Thread의 근본적 한계

한계 상세 설명
비싼 생성 비용 스레드를 만들 때마다 JVM은 OS 스레드를 요청 → 리소스 소모 큼
제한된 동시성 처리 수 수천 개 스레드 이상 생성 시 OutOfMemoryError: unable to create native thread 발생
직접 관리 필요 start(), join(), interrupt() 등 직접 호출해야 하고 예외 처리도 복잡
작업 단위가 없음 스레드는 실행 단위가 아님 → 별도 큐나 관리 계층이 필요
Context Switching 비용 많은 스레드가 대기/준비 상태를 반복하면 커널 컨텍스트 스위칭 비용이 커짐
  • 이러한 문제로 더 높은 수준의 동시성 추상화가 필요하게 됩니다.4. 대체 방식
  • 1) Executors(JDK 5+)
  • 문제점: “새 스레드 생성 비용이 높은” + “직접 관리 어려움”
  • 스레드 풀 기반 관리
  • 스레드 재사용, 작업 단위(Task) 큐 관리, 리소스 제어
    ExecutorService executor = Executors.newFixedThreadPool(10);
    executor.submit(() -> System.out.println("Pooled Task"));

2) ForkJoinPool(JDK 7+)

  • 작업을 쪼개서 병렬 처리하는 divide-and-conquer 기반 풀
  • 주로 parallelStream(), RecursiveTask 등에 사용됨
기존 구조 한계
ThreadPoolExecutor 병렬 분할 작업에 비효율적. 큐가 중앙 집중적 → 병목 발생
Future 블로킹 기반 → get() 호출 시 점유 자원 낭비
병렬 작업 직접 스레드 처리 재귀적 분할 작업에 비효율적 (예: 머지소트, DFS, 큰 루프 나누기 등)

3) CompletableFuture(JDK 8+)

  • 문제점: 콜백 지옥, Future.get() 블로킹, 흐름 제어 어려움
  • 논 블록킹 비동기 조합 및 병렬 작업을 함수형 스타일로 표현
  • 콜백 체이닝, 에러 핸들링 내장
  • 복잡한 스레드 관리를 위해 대체됨
    CompletableFuture.runAsync(() -> doSomething());

4) Virtual Threads(Project Loom, JDK 21+)

  • 문제점: 스레드 수 제한 극복이 필요함
  • OS 스레드가 아닌, JVM이 관리하는 경량 스레드
  • 수십만 개 생성 가능(OS 리소스 거의 사용하지 않음)
  • 자원 고갈(OutOfMemoryError)로 인해 대체
    Thread.startVirtualThread(() -> {
      System.out.println("Lightweight task");
    });

내부 구조 및 특성 비교

항목 Thread ExecutorService ForkJoinPool CompletableFuture VirtualThread
도입 시기 Java 1.0 Java 1.5 Java 7 Java 8 Java 21
주요 목적 기본 동시성 제어 작업 분산 및 재사용 병렬 분할 정복 비동기 논블로킹 대규모 동시 처리
생성 비용 높음 (OS 스레드) 보통 (풀 기반) 보통 (풀 기반) 낮음 (논블로킹) 매우 낮음 (경량)
작업 큐 직접 구현 공유 큐 (BlockingQueue) 각 스레드 Deque (Work Stealing) 없음 (비동기 체인) 없음 (JVM 스케줄링)
스레드 관리 직접 생성/관리 스레드 풀 자동 관리 워크 스틸링 기반 풀 내부 풀 사용 JVM Scheduler가 관리
블로킹 여부 블로킹 블로킹 비블로킹 (권장) 논블로킹 블로킹 가능 (JVM이 처리)
확장성 낮음 중간 높음 (CPU-bound에 적합) 높음 (조합 가능) 매우 높음 (수십만 개)
적합한 작업 단순 병렬 작업 일반 병렬 처리 재귀적 병렬 분할 작업 비동기 로직 조합 대규모 IO 중심 동시성
               +-------------------------+
               |        Thread           |
               +-------------------------+
                            ▲
                            |
           +----------------+----------------+
           |                                 |
+---------------------+       +---------------------------+
|  Executor (인터페이스) |       |      ForkJoinPool         |
+---------------------+       +---------------------------+
           ▲                                 ▲
           |                                 |
+---------------------+       +---------------------------+
| ThreadPoolExecutor  |       | CompletableFuture         |
+---------------------+       +---------------------------+
           ▲                                 ▲
           |                                 |
           +-------------+-------------------+
                         |
            +-------------------------------+
            | Executors.newVirtualThread... |
            +-------------------------------+
  • Thread는 모든 동시성 API의 가장 기초가 되는 실행 단위입니다.
  • Executor는 작업을 실행하는 전략(예: 스레드풀, 가상 스레드)을 추상화한 인터페이스입니다.
  • ThreadPoolExecutor, ForkJoinPool은 Executor의 구현체로 실제 실행 전략을 담당합니다.
  • CompletableFuture는 Executor에 태스크를 전달하고 비동기 흐름을 연결하는 고수준 API입니다.
  • Java 21 이후에는 Executor에 Virtual Thread 기반 구현체를 주입해 경량 동시성을 구현할 수 있습니다.

사용 예 요약

  • Thread: 직접 스레드 생성 필요할 때 (학습/실험)
  • ExecutorService: 다수 작업을 효율적으로 처리 (실무 기본)
  • ForkJoinPool: 정렬, 탐색, 집계 등 CPU 병렬 처리
  • CompletableFuture: API 연동, 이벤트 처리 등 비동기 조합
  • VirtualThread: 웹 요청 처리, 대량 IO, 코루틴 구조 대체

결론

  • Thread는 Java 병렬 처리의 기초였으나, 이제는 실무에서 직접 사용 빈도는 거의 없음
  • 실무에서는 Executors / CompletableFuture / VirtualThread로 추상화 또는 최적화된 처리 사용
  • 대체 방식은 성능, 코드 품질, 관리 편의성을 모두 개선하기 위해 도입됨
728x90
반응형
저작자표시 비영리 (새창열림)

'SW 공학 & 프로그래밍 언어 > Java' 카테고리의 다른 글

[Java] -7 % 3? 정수 나눗셈, % 연산, 모듈러의 진실  (3) 2025.06.18
CAS(Compare-And-Swap) 기법  (0) 2025.05.13
[Java] record는 뭐야?  (1) 2025.03.25
REST API에서 요청 DTO를 매번 만들어야 하나?  (0) 2025.03.25
REST API 설계, GET 방식과 보안이슈  (0) 2025.03.25
'SW 공학 & 프로그래밍 언어/Java' 카테고리의 다른 글
  • [Java] -7 % 3? 정수 나눗셈, % 연산, 모듈러의 진실
  • CAS(Compare-And-Swap) 기법
  • [Java] record는 뭐야?
  • REST API에서 요청 DTO를 매번 만들어야 하나?
크크크크
크크크크
공뷰를 합시다.
    반응형
  • 크크크크
    Tom's Note
    크크크크
  • 전체
    오늘
    어제
    • 분류 전체보기 (130)
      • IT 지식 (6)
      • CS (66)
        • 알고리즘 & 자료구조 (19)
        • 운영체제 (41)
        • 네트워크 (1)
        • 데이터베이스 (5)
      • 보안 (6)
      • SW 공학 & 프로그래밍 언어 (5)
        • Java (28)
        • 디자인 패턴 (1)
        • 형상관리 (2)
        • 톰캣(WAS) (2)
        • SW 방법론 (3)
        • 스프링부트 (5)
      • 시스템 설계 (4)
        • Docker (2)
      • 자격증 (2)
  • 블로그 메뉴

    • 링크

    • 공지사항

    • 인기 글

    • 태그

      chmod
      which
      java
      DTO
      ADsP
      cifs
      man
      apropos
      자바
      usermod
      DI
      1급
      문제해결
      /etc/passwd
      단반향
      docker
      분석기법
      스프링부트
      su
      passwd
      알고리즘
      암호설정
      whereis
      리눅스
      비트연산
      whatis
      REST API
      불변
      Chage
      2차
    • 최근 댓글

    • 최근 글

    • 250x250
    • hELLO· Designed By정상우.v4.10.3
    크크크크
    Thread와 대안들
    상단으로

    티스토리툴바