알면 좋은것

MultiThread 정리

HJ922 2024. 12. 2. 01:03

김영한님의 MultiThread의 강의를 수강했다.

그동안 여러 프로젝트를 진행하며 느꼈던 점과 공부하면서 배운점을 간단하게 정리한다.

 

아래의 개념을 잘모르겠으면 보고오자.

쓰레드,프로세스,코어 간단 정리

 

[CS] 멀티 프로세서? 멀티 프로세스? 멀티쓰레드?

멀티 프로세서, 멀티 프로세스, 멀티쓰레드는 CS를 학습하면서 잘 안와닿아서, 헷갈렸던 개념들이다.여태까지 경험하고 학습해온 내용을 바탕으로 가장 이해하기 쉽게 설명하고자한다.먼저 위

hjustin.tistory.com

public static void main(String args)

 

1. Main 스레드 생성

우리는 코딩 테스트를 할때나, Springboot를 이용한 프로젝트를 진행할때

항상 위의 메서드를 실행한다.

이때 public static void main은 메인 스레드에서 실행되는 함수이다.

 

Java도 프로세스로 이루어져있고 하나의 프로그램이다.

동작하기 위해서는 스레드가 필요하다.

이때 제일 먼저 생기는 쓰레드는 main 스레드 이다.

 

 

2. Thread 동작

메인 스레드는 스택 프레임단위로 동작을 저장한다.

Stack Frame

그림에서 보다시피, 스택프레임 한개가 한개의 메소드이다.

예를들어 다음의 코드가 있다고 가정하자.

public class Gugudan {

    // 구구단 출력 함수
    public static void printGugudan() {
        for (int i = 2; i <= 9; i++) { // 2단부터 9단까지
            System.out.println("== " + i + "단 ==");
            for (int j = 1; j <= 9; j++) { // 각 단의 곱셈
                System.out.println(i + " x " + j + " = " + (i * j));
            }
            System.out.println(); // 단 간 공백 추가
        }
    }

    // 메인 함수
    public static void main(String[] args) {
        System.out.println("구구단 출력 시작");
        printGugudan(); // 구구단 함수 호출
        System.out.println("구구단 출력 종료");
    }
}

 

위의 코드는 다음과 같이 동작한다.

1. Main 스레드 생성

2. Main 함수 넣기(스택 프레임 1)

3. printGugudan 함수 넣기 (스택 프레임 2)

 

스택의 자료구조로 쌓이며 알다시피 역순으로 실행된다.

스레드는 마지막 스택 프레임이 비워지면 종료된다.

그래서 메인함수가 끝나면 프로그램이 종료되는 것이다.

 

3. 멀티 스레드

우리가 방금 작성한 코드에서는 하나의 스레드에 여러 개의 스택프레임을 쌓아 실행했다.

이를 싱글 스레드, 단일 스레드 라고 한다. (Main 스레드 하나로만 진행해서)

 

만약 위의 작업 중간에 다른 작업을 동시에 진행하고 싶으면 하나 더 만들어야 하는데,

이를 멀티 스레드라고 한다.

 

// 구구단 클래스
public class GugudanWithThread {

    // 2단부터 9단 출력
    public static void printGugudan2to9() {
        for (int i = 2; i <= 9; i++) {
            System.out.println("== " + i + "단 ==");
            for (int j = 1; j <= 9; j++) {
                System.out.println(i + " x " + j + " = " + (i * j));
            }
            System.out.println();
        }
    }

    // 10단부터 19단 출력
    public static void printGugudan10to19() {
        for (int i = 10; i <= 19; i++) {
            System.out.println("== " + i + "단 ==");
            for (int j = 1; j <= 9; j++) {
                System.out.println(i + " x " + j + " = " + (i * j));
            }
            System.out.println();
        }
    }

    // 메인 함수
    public static void main(String[] args) {
        System.out.println("구구단 출력 시작");

        // 10단부터 19단 출력하는 쓰레드
        Thread thread10to19 = new Thread(() -> {
            printGugudan10to19();
        });

        // 쓰레드 시작
        thread10to19.start();

        // 2단부터 9단 출력
        printGugudan2to9();

        // 쓰레드가 끝날 때까지 대기
        try {
            thread10to19.join();
        } catch (InterruptedException e) {
            System.out.println("쓰레드가 인터럽트되었습니다: " + e.getMessage());
        }

        System.out.println("구구단 출력 종료");
    }
}

 

 

위의 코드가 복잡해보일 수 있겠지만 핵심은 단 한줄이다.

Thread thread10to19 = new Thread(() -> { printGugudan10to19();});

 

람다식으로 쓰레드의 동작방식까지 한번에 정의하여 알아보기는 어려울 수 있다.

쉽게 동작을 설명하겠다. 이때 새로운 스레드를 A  스레드라고 명칭한다.

1. main 스레드 생성

2. main 스레드가 A 스레드 생성

3. main 스레드가 A 스레드 동작 시킴

4. main 스레드 동작,  A 스레드 동작

4-1 main 스레드의 스택에서 thread2to9 함수 동작 A스레드의 스택에서 thread10to19 함수 동작

5. main 스레드는 A 스레드 끝날때까지 기다림( thread10to19.join() 함수의 기능)

6. main 스레드 종료,  A 스레드 종료

 

간단하지 않은가?

쉽게 말해서, 필요할때 스레드를 만들고 기능을 정의해서 실행만 시켜주면 된다.

중간 중간 멈추고 싶을때 interrupt를 쓴다던지, 다른 조건을 쓰고싶을때 join을 건다던지.

 

코어의 성능이 좋으면 스레드를 많이 써서 문제를 병렬로 해결할 수 있다.

이때 스레드를 어떻게 효율적으로 사용할 것인가에 대한 고민거리들을 스레드 스케줄러가 진행하고,

CS에서 배웠던 라운드로빈, First-Come, First-Served (FCFS), Shortest Job First (SJF) 것들이 있는것이다. 

 

 

=======================================================================================

추후 관련 게시물 업로드 예정

하나의 공유자원에 여러 스레드가 접근했을때 데이터 정합성에 문제가 생길 수 있고 이때 사용하는 개념이

volatile, Synchronized, (LockSupport) Lock 이다.

위의 개념들을 사용할때 생기는 문제점, 개념들이

Semaphore,SpinLock,DeadLock,Mutex,Critical Section 등등 이며

 

문제점을 해결하고 장점을 모아 만든 것이 BlockingQueue라는 클래스이다.

 

BlockingQueue를 사용하여 현실 세계의 문제를 더 효율적으로 해결하기위해 고안한 개념이 스레드 풀이다.

스레드 풀이란 미리 스레드를 만들어 놓는 것이다.

(사용할때마다 스레드를 만들고 삭제하면 오버헤드가 심함)

 

ExecutorService,ThreadPoolExecutor, callable, Future등 다양한 클래스와 인터페이스가 존재한다.