스레드와 멀티스레드

2025. 7. 7. 15:29·JAVA

스레드는 왜 필요한가?

 

과거 CPU는 한 번에 하나의 동작만 처리할 수 있었습니다.

그러나 사용자는 음악을 들으면서 웹 검색을 하거나, 영상 통화 중에 사진을 전송하는 등 여러 작업을 동시에 실행하기를 원합니다.

이를 실현하기 위한 방법이 바로 멀티스레드입니다.

멀티스레드는 하나의 프로그램에서 여러 작업을 동시에 처리할 수 있도록 해주는 기술로, 자바를 포함한 현대의 언어 대부분에서 기본적으로 지원됩니다.


프로세스와 스레드

 

  • 프로세스(Process): 실행 중인 하나의 프로그램입니다.
  • 스레드(Thread): 프로세스 내에서 실제로 작업을 수행하는 실행 흐름입니다.

하나의 프로세스는 여러 개의 스레드를 가질 수 있으며, 여러 작업을 동시에 처리하기 위해 스레드를 사용합니다. 예를 들어, 음악 앱에서 한 스레드는 노래 재생을, 또 다른 스레드는 UI 갱신을 담당할 수 있습니다.


스레드는 메모리를 어떻게 사용하는가?

 

스레드는 아래와 같은 메모리 구조를 가집니다:

  • 공유하는 영역
    • 코드 영역: 프로그램의 실행 코드
    • 힙 영역: 객체가 저장되는 공간
  • 독립적인 영역
    • 스택 영역: 각 스레드의 지역 변수와 메서드 호출 정보

스레드는 힙에 있는 객체는 공유하지만, 스택은 각각 독립적으로 유지됩니다. 이는 스레드 간 자원 충돌의 가능성을 내포하기 때문에, 스레드 안전(safety)이 중요한 이슈가 됩니다.


스레드의 생명주기

 

스레드는 아래와 같은 상태를 거치며 실행됩니다:

  • NEW: 스레드가 생성된 상태 (start() 호출 전)
  • RUNNABLE: 실행 준비 완료, CPU 할당 대기 중
  • RUNNING: CPU가 할당되어 실제 실행 중
  • WAITING / TIMED_WAITING: 대기 상태 (sleep, wait 등)
  • TERMINATED: 실행 종료

이러한 상태 전이 과정은 JVM과 OS가 함께 관리합니다.

**참고로 자바의 스레드와 os 스레드는 1:1 매핑 관계를 가집니다.**


멀티스레드 환경에서 발생할 수 있는 문제들

 

1. 가시성 문제 (Visibility Problem)

 

한 스레드가 변경한 공유 변수의 값이 다른 스레드에게 바로 보이지 않는 문제를 말합니다. 이는 CPU 캐시, 스레드 로컬 캐시 등으로 인해 메모리의 일관성이 깨지면서 발생합니다.

 

해결 방법:

  • volatile 키워드
    static volatile boolean flag = false;

    변수에 volatile을 선언하면 모든 스레드는 해당 변수 값을 메인 메모리에서 직접 읽고 쓰도록 강제합니다. 이를 통해 최신 값을 항상 볼 수 있게 됩니다.

 

  • synchronized 키워드
    synchronized(lock) {
        flag = true; // 변경됨 → 다른 스레드가 볼 수 있음
    }

    synchronized 블록 안의 코드는 락(lock)을 획득해야 실행할 수 있으며, 락을 획득하거나 해제할 때 JVM은 메모리를 동기화합니다. 이로써 가시성과 더불어 원자성까지 확보할 수 있습니다.

2. 레이스 컨디션 (Race Condition)

 

여러 스레드가 동일한 데이터를 동시에 수정하려고 할 때 발생하는 문제입니다. 실행 순서에 따라 결과가 달라질 수 있어 매우 위험합니다.

예: 동시에 출금 요청을 처리하는 코드에서, 두 스레드가 같은 계좌의 잔고를 동시에 읽고 각각 갱신하면 데이터가 꼬일 수 있습니다.

스레드 A: withdraw(100)
스레드 B: withdraw(200)

1. A: balance 읽음 → 1000
2. B: balance 읽음 → 1000
3. A: balance - 100 → 900
4. B: balance - 200 → 800
5. A: balance에 900 저장
6. B: balance에 800 저장 ← 💥 여기서 문제 발생!

결과: 잔고가 900이어야 하는데 800이 됨 → 이걸 RaceCondition 이라고 합니다, 누가 먼저 저장하느냐에 따라 결과가 바뀜 (비결정적)

 

해결 방법:

  • synchronized 키워드를 사용해 코드에 락을 걸고, 한 번에 하나의 스레드만 접근하게 제한합니다.
  • 객체에 락을 걸어 스레드 간의 접근 충돌을 방지합니다. 예를 들어, synchronized 메서드는 해당 객체 전체에 락을 걸며, synchronized 블록은 특정 객체에만 락을 겁니다.

그럼 락만 걸면 동시성 문제는 다 해결될까?

 

  • 장점
    • 데이터 일관성 유지
    • Race Condition 방지
  • 단점
    • 성능 저하 (한 번에 한 스레드만 실행)
    • 데드락(Deadlock) 위험

데드락 문제가 발생할 수 있습니다.


데드락이란?

 

데드락은 두 개 이상의 스레드가 서로 자원을 점유하고, 상대방이 가진 자원을 기다리는 상황으로, 모든 스레드가 멈추는 심각한 상태입니다.

 

데드락이 발생하기 위한 조건은 다음 네 가지입니다:

  1. 상호 배제 (Mutual Exclusion)
    자원은 한 번에 하나의 스레드만 점유 가능
  2. 점유 대기 (Hold and Wait)
    자원을 점유한 채 다른 자원을 기다림
  3. 비선점 (No Preemption)
    자원을 강제로 뺏을 수 없음
  4. 환형 대기 (Circular Wait)
    서로가 가진 자원을 순환적으로 기다리는 구조

이 조건 중 하나라도 깨면 데드락은 발생하지 않습니다.


데드락 해결 방법 예시:

  • 락 획득 순서를 항상 동일하게 유지
  • 락 타임아웃 설정 (예: tryLock(timeout))
  • 운영체제 수준에서 데드락 감지 알고리즘 사용

정리

스레드는 프로그램 내에서 실행 흐름을 나누어 병렬 작업을 가능하게 해줍니다. 그러나 멀티스레드 환경에서는 가시성 문제, 동기화 이슈, Race Condition, 데드락 같은 문제들이 발생할 수 있으므로 반드시 적절한 동기화 도구(volatile, synchronized, Lock, Atomic 등)를 통해 안정적으로 제어해야 합니다.

멀티스레드는 성능을 향상시킬 수 있는 강력한 도구이지만, 그만큼 복잡성과 책임도 따릅니다. 동시성을 설계할 때는 항상 "공유 자원", "실행 순서", "락의 범위와 순서"에 신경 써야 합니다.

참고문헌

  1. https://docs.oracle.com/javase/tutorial/essential/concurrency/
  2. https://docs.oracle.com/javase/specs/jls/se17/html/jls-17.html

'JAVA' 카테고리의 다른 글

G1 GC vs CMS GC  (2) 2025.07.26
GC(Garbage Collection)  (2) 2025.07.26
Stack vs Heap  (1) 2025.07.10
JMM  (0) 2025.07.07
JIT(Just-In-TIme) Complination  (4) 2025.07.07
'JAVA' 카테고리의 다른 글
  • GC(Garbage Collection)
  • Stack vs Heap
  • JMM
  • JIT(Just-In-TIme) Complination
moonwhistle
moonwhistle
  • moonwhistle
    OrangeBanana
    moonwhistle
  • 전체
    오늘
    어제
    • 분류 (113)
      • [Spring] - Study (11)
        • CS (0)
        • Project - 모각밥(모여서 각자 밥먹기) (7)
        • Project - CoinFlow(비트코인 차트) (4)
      • 오픈소스 (1)
      • 📖 DB (1)
      • JAVA (6)
      • 우아한테크코스[프리코스] (15)
      • [Spring] - 멘토링 (30)
        • 미션 (13)
        • 개념 (16)
      • 알고리즘 (2)
      • 💬 생각생각 (3)
        • F-lap (2)
      • 통신 (34)
        • 네트워크 프로토콜 (18)
        • 데이터통신 (16)
      • 용접 (8)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
    • 카테고리
    • 초록스터디
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    data
    volatile
    jmm
    Java
    설계
    KAFKA
    F-Lab
    Synchronized
    Flow
    GC
    후기
    에프랩
    트랜잭션
    동시성
    코인
    spring
    garbage collection
    멀티모듈
    multimodule
    redis
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.4
moonwhistle
스레드와 멀티스레드
상단으로

티스토리툴바