🐯
경민민 IT 핸드북
  • Orientation
    • 전달사항
    • 복습방법
    • 수료한 선배의 한마디
    • 간단 자기소개
    • 스터디
  • 백엔드
    • Java
      • 1장 프로그래밍 기초
      • 2장 자바 메모리구조
        • 1. Stack
        • 2. Heap
      • 6장 객체
      • 8장 상속
      • 9장 다형성
      • 10장 추상클래스와 인터페이스
      • 13장 Generic
      • 14장 Thread
      • 15장 Network
      • 16장 Lamda
        • 1. 내부 클래스 (Inner Class)
          • DTO , VO, Builder Pattern
        • 2. 람다 표현식 (Lambda Expression)
        • 3. 스트림 API (Stream API)
          • Optional
      • 17장 Enum
  • 프론트
    • Node.js
    • Java Script
      • ES6+
        • Node.js로 자바스크립트 실행
        • let , const , var
        • Destructuring문법
          • Rest(...) 문법
        • Arrow Function
        • 모듈
        • ETC
    • Type Script
      • 개요
      • TS설치 및 환경설정
      • 타입스크립트 기본
        • 기본 자료형들과 타입추론
        • Object, Array , Tuple
        • Any, Unknown, Union Type
        • Function Type
          • Type Assertion && Narrowing
          • Never type
        • Type Aliases와 Interface
        • 리터럴 타입
        • 함수 추가 문법
        • Class문법
        • 객체 타입 추가 문법
        • 실습문제 1차
        • 실습문제 2차
        • 실습문제 3차
    • React
      • 개요
      • SPA 와 MPA
        • SEO(작성예정)
      • 리액트 프로젝트 생성(18.3.1.ver)
        • HTML + react 샘플
        • CRA 와 Vite 비교
      • 리액트 개념들
        • Component
          • 클래스 컴포넌트(작성예정)
          • 함수형 컴포넌트(작성예정)
        • JSX
        • React Virtual Dom
          • Reconciliation
        • hook
          • useState
        • 리액트 데이터 전달
          • FLUX
      • 백엔드 서버 연동
        • 비동기요청
        • 웹소켓
        • Promise(작성예정)
      • 실습문제 1
      • 실습문제 2
      • 실습문제3
      • 실습문제4
  • 프레임워크
    • Spring
      • Spring 개발환경 구축
        • 프로젝트 환경설정
        • 프로젝트 생성
          • MVC Project 생성이슈
        • Maven 설정
        • web.xml 설정
        • Spring Bean Configuration.xml 설정
      • Spring Legacy Project
        • Spring 요청 및 응답 흐름
        • Spring 주요 Annotation
          • 의존성 주입방식의 차이점
          • @ModelAttribute와 유효성검사
          • 비동기처리
          • 스프링 예외처리
        • Logging
        • Pagination
        • Spring File 업로드 및 다운로드
        • Spring WebSocket
        • Spring AOP
      • Spring 라이브러리들(작성예정)
        • Lombok
        • Maven
        • MyBatis
      • Spring 구성 모듈(작성예정)
      • 스프링 과제
    • Spring Boot
      • Spring Boot 개발환경 구축
      • 스프링 부트 프로젝트 생성방법들
        • 프로젝트에서 사용하는 의존성들
      • 스프링 프로젝트 구조
        • SpringBootApplication
      • application.properties
      • Cross Origin
        • CORS
      • WebSocket
        • Stomp(작성중)
      • 로그인(작성중)
      • Spring Security(작성중)
      • 실습문제 Select
      • 실습문제 Update
      • 실습문제 Delete
  • 형상관리(Git)
    • GitHub설정
    • SourceTree를 활용한 깃허브 연동
      • 소스트리 설치
      • Clone
      • Branch
        • Branch Protection rules
          • Branch Protection Rules 상세규칙
        • Rebase 와 Squash (작성예정)
      • Team Project 설정
        • 팀장 프로젝트 셋팅
          • Collaborator
          • .gitignore 설정
        • 팀원 프로젝트 셋팅
        • 공통 프로젝트 진행
  • 프로젝트
    • 진행순서
      • 요구사항 분석 단계
        • 유용한 사이트
      • 프로그램 설계 단계
        • 유용한 사이트
      • 프로그램 구현단계
        • SourceTree를 활용한 Team Project설정
      • 테스트 단계
  • 배포
    • AWS-EC2 배포 연습
    • DevOps
      • IT시스템의 변화와 DevOps
      • DevOps 라이프사이클
    • 젠킨스
      • 도커
        • 도커 설치 방법
        • 도커 기본 명령어들
      • 젠킨스 설치
      • 젠킨스 프로젝트 생성
      • 젠킨스 소스코드 통합 - Github
      • 젠킨스 빌드 설정 - Maven
      • 배포 서버 구축하기
      • 파이프라인 구축
      • AWS 서버 생성
        • AWS 인스턴스 생성
        • AWS - Zenkins 연동
        • AWS - 배포서버 연동
        • AWS - Jenkins CI/CD파이프라인 구축
  • 유용한 사이트 모음
  • SQL
    • SQLD
      • 데이터 모델링의 이해 - 스키마
      • 데이터 모델링의 이해 - ERD
      • 데이터 모델링의 이해 - 정규화
      • 데이터 모델링의 이해 - NULL
      • SQL 기본 및 활용 - WINDOW FUNCTION
    • Oracle
      • 1장 개요
      • 2장 SQL
  • LLM 서비스
    • 1장 LLM에 대한 이해
    • 2장 프롬프트 엔지니어링
      • 프롬프트와 프롬프트 엔지니어링
      • GPT PlayGround
      • 프롬프트 작문 유형
      • 기본 프롬프트 엔지니어링 태크닉
      • 고급 프롬프트 엔지니어링 태크닉
        • ReAct Prompting
        • Active-Prompt
        • Reflexion
        • Graph Prompt
      • OpenAI API설정
      • OpenAI를 활용한 프롬프트 엔지니어링 실습
        • 실습 프롬프트
    • 3장 Lang Chain 프레임워크
      • LangSmith 프레임워크
        • LangSmith를 활용한 LangChain 모니터링 설정
      • LangChain 실습 1 - Prompt
        • 실습 코드
      • LangChain 실습 2 - LLM 캐시와 메모리
    • 4장 RAG
      • Document Loader - 문서 로더
      • Text Splitter - 텍스트 분할
      • Embedding - 임베딩
      • Vector Store - 벡터 저장소
      • Retriever - 검색기
      • ReRanker - 재평가자
      • RAG
Powered by GitBook
On this page
  • 1. 스레드?
  • 2. 쓰레드 생성 방법
  • 3. 스레드 스케줄링
  • 4. 동기화(Synchronization)
  • 5. 쓰레드의 생명 주기 (Life Cycle)와 제어메서드
  • 6. 스레드 풀
  1. 백엔드
  2. Java

14장 Thread

1. 스레드?

프로그램 vs 프로세스 vs 쓰레드

  • 프로그램 : 프로세스를 실행할 수 있는 파일로, cpu를 할당 받기 전의 상태를 의미한다.

  • 프로세스 : 실행 중인 프로그램으로 작업을 위한 cpu 자원을 할당 받은 상태. 각 프로세스는 독립된 메모리 공간을 가짐.

  • 스레드: 프로세스 내에서 실제로 작업을 수행하는 실행 단위. 여러 쓰레드가 하나의 메모리 공간(Heap)을 공유해서 사용 한다. 중심 쓰레드를 main스레드라 부르며 , 그 외의 쓰레드를 서브스레드라고 부른다.

  • 모든 프로세스는 1개 이상의 쓰레드를 가지고 있다.

멀티쓰레드

  • 자바는 한번에 여러 개의 스레드를 실행할 수 있는 멀티쓰레드 환경을 지원한다.

  • 멀티스레드는 하나의 쓰레드가 해야할 작업을 여러개의 쓰레드가 분담하여 작업할 수 있으므로 작업 효율이 향상 된다.

  • 멀티쓰레드로 인해 사용자는 빠른 응답을 받을 수 있다.

  • 멀티쓰레드 환경에서는 자원 공유에 따른 동기화 문제가 발생하며, DeadLock상태가 발생할 수 있으므로 주의해야 한다.

쓰레드의 특징

  • 동시성 (Concurrency)

    • 여러 작업을 교대로 빠르게 처리하여 동시에 여러 작업이 실행되는 것 처럼 보이는 특징.

    • 실제로는 스케쥴러에 의해 실행할 쓰레드를 순간적으로 전환하며 실행하기 때문에 "동시"에 실행되지는 않는다.(싱글 코어 기준)

  • 병렬성 (Parallelism)

    • CPU코어가 여러개인 경우 CPU코어별로 작업내용이 할당 되므로, 코어의 개수만큼 실제로 여러 작업을 동시에 실행한다.

    • 즉, CPU코어가 여러 개라면 쓰레드가 실제로 동시에 여러 작업을 수행할 수 있게 되는 것.

  • 독립성(Isolation)

    • 하나의 스레드에서 발생한 에러는 다른 스레드에 영향을 끼치지 않는다.

    • 개별적인 실행을 보장하기 위해 스레드는 고유한 stack영역을 할당 받는다.


2. 쓰레드 생성 방법

1. Thread 클래스 상속

class MyThread extends Thread {
    public void run() {
        System.out.println("Thread 실행 중...");
    }
}

MyThread t = new MyThread();
t.start();  // 반드시 start()로 실행해야 run()이 별도 쓰레드에서 실행됨
// start() 실행시 쓰레드 생성. 쓰레드가 생성되면 내부의 run()메서드가 실행.

2. Runnable 인터페이스 구현

class MyRunnable implements Runnable {
    public void run() {
        System.out.println("Runnable 실행 중...");
    }
}

Thread t = new Thread(new MyRunnable());
t.start();

3. 람다 표현식 사용

Thread t = new Thread(() -> {
    System.out.println("람다 쓰레드 실행");
});
t.start();

3. 스레드 스케줄링

스레드는 OS에 의해 스케줄링되며, 작업 순서를 개발자가 완전히 제어할 수는 없지만, 우선순위를 설정하거나 데몬 여부(종속설정)를 조절하여 실행 흐름에 관여할 수 있습니다.

1. 우선순위 기반 스케줄링

  • 자바에서 Thread.setPriority(int) 메서드를 통해 우선순위를 설정할 수 있습니다.

  • 우선순위는 1 (MIN_PRIORITY) ~ 10 (MAX_PRIORITY) 사이의 값이며 기본값은 5입니다.

단, 우선순위가 높다고 먼저 실행된다는 보장은 없습니다. JVM은 스케줄링을 운영체제에 위임하기 때문에 결과는 OS마다 다르게 나올 수 있습니다.

2. Round-Robin 방식 (순환 할당)

  • 운영체제가 각 스레드에 동일한 시간만큼 CPU를 할당하는 기본 스케쥴링 방식입니다.

  • 자바의 JVM은 운영체제의 스레드 스케줄러에 위임하므로 Round-Robin은 운영체제의 스케쥴링 방식이라고 볼 수 있습니다.

  • 자바 코드로는 제어할 수 없으며, 같은 우선순위 스레드들 사이에 적용됩니다.

  • 우선순위에서 같은 값을 가진 스레드 사이에서는 Round-Robin이 적용됩니다.

3. 데몬 스레드

  • setDaemon(true) 설정 시 메인 스레드가 종료되면 자동으로 같이 종료됩니다.

  • 백그라운드 작업(로그 저장, 자동 저장 등)에 자주 사용됩니다.


4. 동기화(Synchronization)

멀티스레드 환경에서는 여러 스레드가 동시에 하나의 공유 자원(Heap영역의 객체 등)에 접근할 수 있습니다. 이 상태를 경쟁상태(race condition) 라고 부릅니다. 경쟁상태에서는 데이터 충돌이나 예기치 못한 결과가 발생할 수 있습니다. 이 문제를 방지하기 위해서는 경쟁상태의 자원을 통제하기 위한 통제영역(임계영역)을 지정하기 위해 사용하는 것이 "동기화(synchronization)"입니다.

synchronized

  • synchronized 키워드를 사용하여 특정 메서드나 블록을 한 번에 하나의 스레드만 접근할 수 있도록 제한합니다.

  • synchronized예약어로 여러 스레드가 동시에 접근해서는 안 되는 메서드를 임계 영역 (Critical Section)을 지정한다.

  • 예시코드:

public class Account {
    private int balance = 1000;
    
    // 출금 메서드는 한번에 하나의 스레드만 사용 가능.
    public synchronized void withdraw(int money) {
        if (balance >= money) {
            balance -= money;
            System.out.println(Thread.currentThread().getName() + " 출금 후 잔액: " + balance);
        } else {
            System.out.println(Thread.currentThread().getName() + " 출금 실패 - 잔액 부족");
        }
    }
}
  • 위 예제에서 withdraw 메서드는 임계 영역이므로 synchronized를 사용하여 한 번에 하나의 스레드만 접근하게 합니다.

동기화 비유

  • 여러분들이 화장실을 이용한다고 가정해봅시다. 화장실은 칸당 1명만 들어갈 수 있는 구조로, 누군가 화장실에 들어가면 문을 잠그고(Lock) 사용이 끝나면 문을 열고(UnLock)나옵니다. 이때 화장실 밖에는 화장실칸에 들어가려는 사람들이 순서에 맞춰 대기중일 겁니다.

  • 코딩적 관점에서 보면 화장실 칸은 공유자원으로, 이를 이용하는 사람은 스레드입니다. 화장실을 이용할 때는 한번에 한명의 사람이 이용하듯, 동기화 처리가 완료된 공유자원은 한번에 한개의 스레드만 접근할 수 있습니다. 이때 공유자원을 사용 중 인 스레드는 Lock을 얻고, 사용 완료한 스레드는 UnLock후 다음 스레드가 Lock을 획득합니다.

  • 이 과정을 락(Lock) 메커니즘이라고 부릅니다. 자바에서는 동기화(synchronized )를 통해 락 메커니즘을 구현할 수 있습니다.

동기화의 단점

  • 모든 메서드에 synchronized를 걸면 병목 현상이 발생할 수 있습니다.

  • 병목현상 : 하나의 공유자원에 너무 많은 스레드가 몰려 , 전체 시스템 성능이 하향되는 현상

    • 8차선 도로로 주행하던 차들이 1차선 도로로 주행해야 하는 경우를 생각하시면 됩니다.

  • 스레드 수가 많아질수록 전체 시스템성능이 하향될 수 있으므로 , 반드시 필요한 부분에만 synchronized를 걸어야 합니다.

데드락

  • 두개 이상의 스레드가 서로 락을 얻기 위해 무한으로 대기하는 현상

  • 데드락 예시 코드

public class DeadLockRun {
    // 공유 자원 1
    private static final Buffer buffer1 = new Buffer();
    // 공유 자원 2
    private static final Buffer buffer2 = new Buffer();

    public static void main(String[] args) {
        // 첫 번째 스레드: buffer1 먼저 획득하고 buffer2를 기다림
        Thread thread1 = new Thread(() -> {
            synchronized (buffer1) {
                System.out.println("Thread1 : buffer1에 대한 key 획득");
                synchronized (buffer2) {
                    System.out.println("Thread 1: buffer2에 대한 key 획득");
                }
            }
        });

        // 두 번째 스레드: buffer2를 먼저 획득하고 buffer1d을 기다림
        Thread thread2 = new Thread(() -> {
            synchronized (buffer2) {
                System.out.println("Thread 2: buffer2에 대한 key 획득");
                synchronized (buffer1) {
                    System.out.println("Thread 2: buffer1에 대한 key 획득");
                }
            }
        });

        thread1.start();
        thread2.start();
    }
}

5. 쓰레드의 생명 주기 (Life Cycle)와 제어메서드

생명주기

  1. NEW: new Thread()로 생성만 된 상태

  2. RUNNABLE: start() 호출 후 운영체제에 의해 실행 가능 상태

  3. RUNNING: 실제로 CPU를 점유하고 실행 중인 상태

  4. BLOCKED / WAITING / TIMED_WAITING: 일시 정지된 상태 (sleep, wait, join 등)

    1. BLOCKED ⇒ 동기화 블럭에서 스레드가 Lock을 얻기 위해 대기하는 상태.

  5. TERMINATED: 실행이 종료된 상태

스레드 상태 제어 메서드

  • sleep(ms): 지정 시간 동안 일시 정지 (예: Thread.sleep(1000))

    • 스레드는 RUNNING -> TIME_WAITING->RUNNABLE 상태로 변경

  • join(): 특정 스레드가 끝날 때까지 대기(일시정지)

    • RUNNING -> (WAITING/TIME_WAITING)

  • interrupt(): 일시정지 상태의 스레드를 깨움 (InterruptedException 발생)

    • RUNNING -> TIME_WAITING -> RUNNABLE

  • wait() : 동기화 블록(synchronized)에서 사용되는 스레드 간 통신으로 스레드를 대기상태로 변경

    • RUNNING -> WAITING

  • notify() / notifyAll(): 동기화 블록에서 사용되는 스레드 간 통신으로 WAITING상태의 쓰레드를 깨움

    • WAITING -> RUNNABLE


6. 스레드 풀

  • 스레드를 필요할때마다 생성하는 것이 아닌, 미리 여러개의 스레드를 생성하여 보관해두고, 재사용하는 스레드 저장공간을 스레드 풀이라고 부른다.

  • 스레드가 생성/소멸할때 발생하는 자원의 낭비를 줄일 수 있으며, 사용자의 스레드 요청시 생성 없이 만들어둔 스레드를 즉시 반환할 수 있으므로 빠른 응답이 가능해진다.

    • 치킨가게에서 메뉴주문시 조리된 치킨을 즉시 주는 것과, 주문이 들어간 후 치킨을 만들어서 주는 것의 차이.

  • 사용자의 요청이 들어올때 마다 스레드를 계속 생성한다면, 과도하게 많은 스레드가 생성될 수 있으나, 미리 생성할 총 스레드의 갯수를 정해두고 미리 생성한다면 이러한 현상을 방지할 수 있음.

Previous13장 GenericNext15장 Network

Last updated 9 days ago