개발/자바

Thread 설정 및 주요 메서드

피터JK 2025. 2. 17. 14:22
728x90

Java에서 Thread 관련 설정 및 주요 메서드를 정리해 보겠습니다.


1. Thread 생성 및 실행 방법

(1) Thread 클래스를 상속하여 생성

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

    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.start(); // 새로운 스레드 시작
    }
}

(2) Runnable 인터페이스를 구현하여 생성

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

    public static void main(String[] args) {
        Thread thread = new Thread(new MyRunnable());
        thread.start();
    }
}

2. 주요 메서드

메서드 설명
start() 새로운 스레드를 생성하고 실행 (run() 호출)
run() 스레드 실행 로직 (직접 호출하면 단순한 메서드 실행)
join() 해당 스레드가 종료될 때까지 현재 스레드 대기
sleep(ms) 현재 스레드를 지정된 밀리초(ms) 동안 정지
interrupt() 스레드 중단 요청 (예외 발생 가능)
isInterrupted() 스레드가 인터럽트 상태인지 확인
yield() 실행 중인 스레드를 일시적으로 멈추고 다른 스레드에게 CPU 양보
setDaemon(true) 데몬 스레드로 설정 (메인 스레드 종료 시 자동 종료)
getName() 스레드 이름 가져오기
setName("이름") 스레드 이름 설정
getId() 스레드 고유 ID 가져오기
getState() 현재 스레드 상태 가져오기
getPriority() 스레드 우선순위 가져오기 (1~10)
setPriority(int) 스레드 우선순위 설정 (기본값: 5)

3. Thread 상태 (Lifecycle)

스레드는 아래의 6가지 상태를 가질 수 있습니다.

상태 설명
NEW start() 호출 전 (아직 실행되지 않음)
RUNNABLE 실행 가능한 상태 (CPU 점유 대기)
BLOCKED 다른 스레드가 자원을 점유 중이라 대기
WAITING wait() 호출로 무기한 대기
TIMED_WAITING sleep(ms), join(ms), wait(ms) 호출로 일정 시간 대기
TERMINATED 스레드 종료 상태

🔹 스레드 상태 확인

System.out.println(thread.getState()); // 현재 스레드 상태 출력

4. Thread 우선순위 설정

스레드는 기본적으로 5의 우선순위를 가지며, 1~10까지 설정 가능함.

Thread thread = new Thread(new MyRunnable());
thread.setPriority(Thread.MAX_PRIORITY); // 10
thread.setPriority(Thread.MIN_PRIORITY); // 1
thread.setPriority(Thread.NORM_PRIORITY); // 5 (기본값)

👉 하지만 OS 스케줄러가 결정하기 때문에 반드시 높은 우선순위가 먼저 실행된다는 보장은 없음.


5. Thread 동기화 (Synchronization)

멀티스레드 환경에서는 공유 자원 보호를 위해 동기화가 필요합니다.

(1) synchronized 키워드 사용

class SharedResource {
    synchronized void printMessage(String message) {
        System.out.println(message);
    }
}

(2) ReentrantLock 사용

import java.util.concurrent.locks.ReentrantLock;

class SharedResource {
    private final ReentrantLock lock = new ReentrantLock();

    void printMessage(String message) {
        lock.lock();
        try {
            System.out.println(message);
        } finally {
            lock.unlock();
        }
    }
}

6. Thread 종료 방법

(1) interrupt() 활용

class MyThread extends Thread {
    public void run() {
        while (!Thread.currentThread().isInterrupted()) {
            System.out.println("Thread 실행 중...");
        }
        System.out.println("Thread 종료됨");
    }
}

🔹 interrupt() 호출 시 isInterrupted()가 true가 되어 루프 종료

(2) volatile 플래그 활용

class MyThread extends Thread {
    private volatile boolean running = true;

    public void run() {
        while (running) {
            System.out.println("Thread 실행 중...");
        }
        System.out.println("Thread 종료됨");
    }

    public void stopThread() {
        running = false;
    }
}

🔹 volatile 변수 변경을 통해 안전한 종료 가능


7. 데몬 스레드

  • setDaemon(true)를 설정하면 메인 스레드 종료 시 자동 종료됨.
  • 주로 백그라운드 작업에 사용됨 (ex. GC, 로그 처리).
class MyDaemonThread extends Thread {
    public void run() {
        while (true) {
            System.out.println("데몬 스레드 실행 중...");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                break;
            }
        }
    }

    public static void main(String[] args) {
        MyDaemonThread thread = new MyDaemonThread();
        thread.setDaemon(true);
        thread.start();

        try { Thread.sleep(3000); } catch (InterruptedException e) {}
        System.out.println("메인 스레드 종료");
    }
}

📌 주의: 데몬 스레드는 finally 블록이 실행되지 않을 수 있음.


8. Thread Pool (ExecutorService) 활용

스레드를 효율적으로 관리하려면 Thread Pool을 사용하는 것이 좋음.

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

class MyTask implements Runnable {
    public void run() {
        System.out.println(Thread.currentThread().getName() + " 실행 중...");
    }
}

public class ThreadPoolExample {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(3); // 3개의 스레드 풀 생성

        for (int i = 0; i < 5; i++) {
            executor.submit(new MyTask());
        }

        executor.shutdown(); // 작업 종료
    }
}

장점:
✔️ 직접 Thread 객체를 생성하지 않아도 됨
✔️ 스레드 재사용 가능 (비용 절감)
✔️ 동시 실행 제한 가능


9. Callable과 Future (값 반환)

  • Runnable은 반환값이 없지만, Callable은 값 반환 가능.
import java.util.concurrent.*;

class MyCallable implements Callable<String> {
    public String call() {
        return "Callable 실행 완료!";
    }
}

public class CallableExample {
    public static void main(String[] args) throws Exception {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        Future<String> future = executor.submit(new MyCallable());

        System.out.println(future.get()); // 결과 반환
        executor.shutdown();
    }
}

📌 get()은 결과가 나올 때까지 블로킹됨.


결론

  • Thread vs Runnable → Runnable 권장
  • synchronized vs ReentrantLock → ReentrantLock이 더 유연함
  • interrupt() vs volatile → 둘 다 종료에 활용 가능
  • 직접 Thread 생성 vs ExecutorService → Thread Pool이 효율적
  • Runnable vs Callable → Callable은 값 반환 가능

💡 멀티스레드는 성능 최적화뿐만 아니라 동기화 문제도 신경 써야 하므로 주의가 필요합니다! 🚀

728x90