Notice
Recent Posts
Recent Comments
«   2026/04   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30
Tags
more
Archives
Today
Total
관리 메뉴

Spring & Java

쓰레드 ( Thread ) 본문

JAVA 개념 확장

쓰레드 ( Thread )

dev.hyuck 2025. 12. 18. 17:23

예외 ( Exception )과 예외처리 (try-catch)

Optional - null을 다루는 법

제네릭 ( Generic )

컬렉션 ( Collection )

람다 ( Lambda )

스트림 ( Stream )

쓰레드 ( Thread )

 

쓰레드 - 프로그램 내에서 독립적으로 실행되는 작은 실행 단위 ( 일꾼 )

싱글 쓰레드 - 하나의 작업을 순차적으로 처리하는 실행 방식 ( 한 명의 일꾼 )

멀티 쓰레드 - 여러 작업을 동시에 처리하는 실행 방식 ( 여러 명의 일꾼 )

 

쓰레드 ( Thread ) 란?

쓰레드를 학습해 봅시다.

● 쓰레드는 프로그램 내에서 독립적으로 실행되는 하나의 작업 단위입니다. ( 한 명의 일꾼 )

● 싱글 쓰레드는 한 번에 하나의 작업만 처리하지만 멀티 쓰레드는 여러 작업을 동시에 처리할 수 있습니다.

● 멀티 쓰레드를 활용하면 여러 작업을 병렬로 수행할 수 있어 성능을 향상 시킬 수 있습니다.

 

싱글 쓰레드 ( Single Thread )

● 싱글 쓰레드는 한 명의 일꾼이 작업을 처리 하는 것과 같습니다.

● 일꾼이 한 명이기 때문에 여러 개의 작업이 있다면 순차적으로 처리 해야 합니다.

● main () 메서드는 프로그램 시작과 동시에 생성되는 하나의 쓰레드 입니다.

● 아래 코드는 main() 메서드 하나의 쓰레드가 작업을 처리하는 예시 입니다.

public class Main {

    public static void main(String[] args) {
        System.out.println("::: main 쓰레드 시작 :::");
        String threadName = Thread.currentThread().getName();
        
        // ✅ 하나의 작업 단위: 숫자를 0 부터 9 까지 출력
        for (int i = 0; i < 10; i++) {
            System.out.println("현재 쓰레드: " + threadName + " - " + i);
            try {
                Thread.sleep(500); // 0.5 초 대기
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // ✅ 추가작업
        for (int i = 0; i < 10; i++) {
            System.out.println("현재 쓰레드: " + threadName + " - " + i);
            try {
                Thread.sleep(500); // 0.5 초 대기
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("::: 작업 끝 :::");
    }
}

 

멀티 ( Multi - Thread ) 와 쓰레드 구현

멀티 쓰레드 예시를 살펴 봅시다.

● 멀티 쓰레드는 작업을 처리할 수 있는 여러 명의 일꾼을 의미 합니다.

● 멀티 쓰레드를 활용해서 여러 작업 ( 0 ~ 9 출력) 을 병렬 (동시)로 처리할 수 있습니다.

● Thread 클래스를 상속받아 쓰레드를 구현할 수 있습니다.

● Thread.run() 메서드를 오버라이드 해서 쓰레드가 수행할 작업을 정의할 수 있습니다.

● 총 세 개의 쓰레드 ( main, thread0, thread1) 병렬로 실행 됩니다.

● main 쓰레드는 thread 0, thread 1 을 생성하고 실행 시킵니다.

● 생성된 thread 0, thread 1 는 0.5초 마다 0 ~ 9 까지 숫자를 출력 합니다.

 

쓰레드를 실행시킬 때 꼭 start() 를 사용하세요 !

start () 는 새로운 쓰레드에서 run () 을 실행하지만 run ()을 직접 호출하면 현재 쓰레드에서 실행 됩니다.

// ✅ Thread 클래스 상속으로 쓰레드 구현
public class MyThread extends Thread {

    @Override
    public void run() {
        String threadName = Thread.currentThread().getName();
        System.out.println("::: " + threadName + "쓰레드 시작 :::");
        for (int i = 0; i < 10; i++) {
            System.out.println("현재 쓰레드: " + threadName + " - " + i);
            try {
                Thread.sleep(500); // 딜레이 0.5 초
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("::: " + threadName + "쓰레드 종료 :::");
    }
}

public class Main {

    public static void main(String[] args) {
        System.out.println("::: main 쓰레드 시작");

        MyThread thread0 = new MyThread();
        MyThread thread1 = new MyThread();

        // 1. thread0 실행
        System.out.println("::: main 이 thread0 을 실행");
        thread0.start();

        // 2. thread1 실행
        System.out.println("::: main 이 thread1 을 실행");
        thread1.start();

        System.out.println("::: main 쓰레드 종료");
    }
}

 

join() - 특정 쓰레드가 끝날 때까지 기다리기

join을 학습해 봅시다.

● join() 은 main() 쓰레드가 다른 쓰레드가 종료될 때까지 기다리게 하는 메서드 입니다.

● join() 을 호출한 쓰레드가 끝날 떄까지 main() 쓰레드가 대기 합니다.

● main() 쓰레드가 너무 빨리 끝나지 않고 모든 작업이 완료된 후 종료되도록 할 때 유용 합니다.

> 응용하면 main() 쓰레드가 총 작업 완료 시간을 측정할 수 있게 만들 수 있습니다.

● 아래의 두 개의 쓰레드 ( thread 0, thread 1) 가 실행되고 끝날 때까지 main () 이 기다립니다.

● 모든 작업이 완료되면 main() 은 종료 됩니다.

public class Main {

    public static void main(String[] args) {
        System.out.println("::: main 쓰레드 시작");
        MyThread thread0 = new MyThread();
        MyThread thread1 = new MyThread();

        // 시작시간 기록
        long startTime = System.currentTimeMillis();

        // 1. thread0 시작
        System.out.println("thread0 시작");
        thread0.start();

        // 2. thread1 시작
        System.out.println("thread1 시작");
        thread1.start();

        // ⌛️ main 쓰레드 대기 시키기
        try {
            thread0.join();
            thread1.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        long endTime = System.currentTimeMillis();
        long totalTime = endTime - startTime;
        System.out.println("총 작업 시간: " + totalTime + "ms");
        System.out.println("::: main 쓰레드 종료");
    }
}

 

Runnable 인터페이스 활용 ( 권장 ) 

Runnable 인터페이스를 활용해 쓰레드를 구현하는 것을 권장 합니다.

1 유지 보수성과 재사용성 향상

2 확장 가능성

 

1. 유지 보수상과 재사용성 향상 됩니다.

● thread 는 쓰레드를 제어하기 위해 존재하는 클래스 입니다.

● thread 클래스를 상속받아 MyThread 를 구현하면 실행 로직과 쓰레드 제어 로직이 결합되어 한 가지 클래스에서 두가지 역학을 담당하게 됩니다.

-> 쓰레드 제오 로직: start(), join, isAlive() 등 

-> 실행 로직 : 숫자 0 ~ 9 출력

-> 하나의 클래스는 하나의 책임만 가지는 것이 유지 보수에 좋습니다.

● Runnable 을 활용하면 실행 로직을 별도의 구현체로 분리할 수 있습니다.

- > Thread 는 쓰레드를 제어하는 역할.

- > Runnable 구현체는 실행 로직을 관리

// ⚠️ Thread는 Thread 제어 역할 그리고 실행로직 두가지를 담당합니다.
public class MyThread extends Thread {

    @Override
    public void run() {
        String threadName = Thread.currentThread().getName();
        System.out.println("현재 시작된 쓰레드: " + threadName);

        for(int i = 0; i < 10; i++) {
            System.out.println("현재 쓰레드 : " + threadName + " - " + i);
            try {
                Thread.sleep(500); // 딜레이 0.5 초
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("종료된 쓰레드: " + threadName);
    }
}

public class MyRunnable implements Runnable {
    @Override
    public void run() {
        String threadName = Thread.currentThread().getName();
        for (int i = 0; i < 10; i++) {
            System.out.println("현재 쓰레드: " + threadName + " - " + i);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class Main {

    public static void main(String[] args) {

        MyRunnable task = new MyRunnable(); // ✅ 하나의 작업 객체 선언

				// ✅ 하나의 작업을 여러 쓰레드에서 공유
        Thread thread0 = new Thread(task); // 작업 객체 공유
        Thread thread1 = new Thread(task); // 작업 객체 공유

				// 실행
        thread0.start();
        thread1.start();
    }
}

 

2. 기존 클래스를 유지하면서 확장 가능

● thread 를 상속해서 MyThread 를 구현하면 다른 클래스를 상속 받지 못합니다.

- > Java는 클래스의 다중 상속을 지원하지 않습니다.

- > Thread를 상속하면 다른 클래스를 상속할 수 없어서 확장성이 떨어집니다.

● Runnable 은 인터페이스이므로 기존 클래스의 기능을 유지하면서 상속을 통해 확장할 수 있습니다.

public class MyNewClass { // ✅ 새로운 클래스 

    public void printMessage() {
        System.out.println("MyClass 기능 실행");
    }
}

public class MyThread extends Thread, MyNewClass{ // ❌ 다중 상속 불가
		...
}

public class MyRunnable extends MyNewClass implements Runnable { // ✅ 다중 상속

    @Override
    public void run() {
        String threadName = Thread.currentThread().getName();
        for (int i = 0; i < 10; i++) {
            System.out.println("현재 쓰레드: " + threadName + " - " + i);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class Main {

    public static void main(String[] args) {

        MyRunnable task = new MyRunnable();

        // ✅ 기존 클래스를 유지하면서 확장해서 활용
        task.printMessage(); 

        Thread thread0 = new Thread(task);
        Thread thread1 = new Thread(task);

        thread0.start();
        thread1.start();
    }
}

 

'JAVA 개념 확장' 카테고리의 다른 글

메모 요점정리 노트  (0) 2025.12.18
스트림 ( Stream )  (0) 2025.12.18
람다 (Lambda)  (0) 2025.12.17
제네릭 ( Generic )  (0) 2025.12.15
컬렉션 ( Collection )  (0) 2025.12.15