Backend/Java

[Java]Thread

해로몬 2024. 11. 1. 17:45

스레드 생성

스레드 생성과 실행은 Thread 클래스와 Runnalbe 인터페이스를 통해 이루어집니다.

 

1. Thread 클래스를 상속받아 생성하기

  • Thread 클래스를 상속하여 새로운 스레드를 생성하는 방법입니다.
  • Thread 클래스의 run() 메서드를 오버라이드하여, 스레드가 수행할 작업을 정의합니다.
  • start() 메서드를 호출하면 새로운 스레드가 생성되어 run() 메서드를 실행합니다.

2. Runnable 인터페이스를 구현하여 생성하기

  • Runnable 인터페이스를 구현하여 스레드를 생성하는 방법입니다.
  • run() 메서드를 오버라이드하여 스레드가 수행할 작업을 정의합니다.
  • Thread 클래스의 생성자에 Runnable을 구현한 객체를 전달하여 스레드를 생성하고, start() 메서드를 호출하여 실행합니다.

스레드 생성 방법의 비교

방법 장점 단점
Thread 클래스 상속 간단하게 스레드를 정의하고 사용할 수 있음 다른 클래스 상속이 불가하므로 확장성 부족
Runnable 인터페이스 구현 다른 클래스를 상속할 수 있어 더 유연한 설계 가능 약간의 코드가 더 필요함

 

구구단

// Gugudan.java
public class Gugudan extends Thread {
    private int dan;

    // 생성자: 구구단의 단을 받아 초기화
    public Gugudan(int dan) {
        this.dan = dan;
    }

    // 스레드가 실행될 때 구구단 출력
    @Override
    public void run() {
        System.out.println(getName() + " 시작");

        for (int i = 1; i <= 9; i++) {
            System.out.println(dan + " * " + i + " = " + (dan * i));
        }
        System.out.println(getName() + " 끝");
    }
}

package com.exam4;

public class ThreadEx {
    public static void main(String[] args) {
        // 스레드를 이용하여 3단과 6단 출력
        Gugudan gugudan1 = new Gugudan(3);
        Gugudan gugudan2 = new Gugudan(6);

        gugudan1.start();
        gugudan2.start();
    }
}

스레드 상태

  • NEW (새로운 상태): 스레드가 생성된 후 아직 start() 메서드가 호출되지 않은 상태입니다.
  • RUNNABLE (실행 가능 상태): start() 메서드가 호출되어 CPU에서 실행 대기 중인 상태입니다. 운영체제의 스케줄러가 CPU를 할당하면 RUNNING 상태로 변경됩니다.
  • BLOCKED (차단 상태): 스레드가 잠금(lock)을 기다리고 있는 상태입니다. 자원을 획득하지 못해 실행되지 못하고 대기하는 상황입니다.
  • WAITING (대기 상태): 스레드가 특정 조건을 만족하기 전까지 대기해야 하는 상태입니다. 예를 들어, 다른 스레드가 작업을 완료할 때까지 대기하는 경우가 이에 해당됩니다.
  • TIMED_WAITING (시간 지정 대기 상태): 지정된 시간이 경과할 때까지 대기 상태에 있는 경우입니다. sleep()이나 wait(timeout) 메서드를 통해 이 상태로 진입할 수 있습니다.
  • TERMINATED (종료 상태): 스레드의 작업이 완료되어 종료된 상태입니다. 종료된 스레드는 다시 시작할 수 없습니다

우선순위 스레드

우선순위(Thread Priority)는 여러 스레드가 동시에 실행될 때 어떤 스레드를 더 우선적으로 실행할지 결정하는 기준이 됩니다. Java에서 스레드는 기본적으로 동일한 우선순위를 가지고 실행되지만, 특정 스레드에 우선순위를 부여하여 운영체제가 우선적으로 실행하도록 유도할 수 있습니다. Java에서는 Thread 클래스의 setPriority 메서드를 통해 스레드의 우선순위를 설정할 수 있습니다.

우선순위 값의 범위

Java에서 스레드의 우선순위는 1부터 10까지의 정수 값으로 설정할 수 있습니다.

  • MIN_PRIORITY (1): 최저 우선순위
  • NORM_PRIORITY (5): 기본 우선순위 (디폴트)
  • MAX_PRIORITY (10): 최고 우선순위


일반적으로 우선순위가 높은 스레드가 더 자주 CPU 시간을 할당받아 먼저 실행될 가능성이 큽니다.

우선순위의 한계

  1. 운영체제 의존적: 스레드 우선순위 설정은 운영체제의 스케줄러에 의존합니다. Java에서 우선순위를 설정하더라도 모든 운영체제가 이를 반드시 반영하지 않기 때문에, 우선순위가 높은 스레드가 항상 먼저 실행되는 것은 아닙니다.
  2. 비결정적 실행: 우선순위는 실행될 가능성을 높여주지만, 실행 순서를 완전히 보장하지 않습니다. 특히, 멀티코어 시스템에서는 각 코어에서 병렬로 실행되기 때문에 실행 순서는 예측하기 어렵습니다.
  3. 다른 스레드 제어 방식과 병행: 우선순위는 효율적인 CPU 사용을 위한 보조적 방법일 뿐이며, 필요에 따라 join(), sleep(), wait() 등 다른 스레드 제어 방법과 병행하여 사용해야 할 때가 많습니다.

예제 : 구구단 출력에 우선순위 적용

public class GugudanPriority extends Thread {
      ...

    public static void main(String[] args) {
        GugudanPriority g3 = new GugudanPriority(3);
        GugudanPriority g6 = new GugudanPriority(6);


        g3.setPriority(Thread.MIN_PRIORITY); // 3단: 최저 우선순위
        g6.setPriority(Thread.MAX_PRIORITY); // 6단: 최고 우선순위

        g3.start();
        g6.start();
    }
}

Thread 메서드 

1.  Thread.currentThread() : 현재 실행 중인 스레드를 반환하는 메서드로, 현재 스레드의 정보를 참조할 수 있도록 합니다.

public class ThreadEx03 {
    public static void main(String[] args) {
        // 현재 실행 중인 스레드의 우선순위 확인
        System.out.println("Current thread priority: " + Thread.currentThread().getPriority());

        // 현재 실행 중인 스레드의 개수 출력
        System.out.println("Active thread count: " + Thread.activeCount());

        // 현재 스레드를 참조하는 객체 생성
        Thread current = Thread.currentThread();

        // 현재 스레드 정보 출력
        System.out.println("Current thread: " + current); // 스레드 객체 출력
        System.out.println("Thread name: " + current.getName()); // 스레드 이름 출력
        System.out.println("Is thread alive: " + current.isAlive()); // 스레드가 활성 상태인지 확인
        System.out.println("Is daemon thread: " + current.isDaemon()); // 데몬 스레드 여부 확인
    }
}

2. Thread.sleep(long millis) : 현재 스레드를 지정된 시간 동안 일시 정지 시키는 메서드입니다. 주어진 시간만큼 스레드가 일시 중단되었다가, 시간이 지나면 자동으로 실행 대기 상태로 돌아갑니다. 

  • 예외처리 : sleep()메서드는 InterruptedException을 발생시킬 수 있으므로, 이를 처리하기 위해 try-catch 블록으로 감싸는 것이 일반적입니다.
public class ThreadEx04 {
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            System.out.println(i); // 현재 숫자 출력
            try {
                Thread.sleep(1000); // 1초 동안 일시정지
            } catch (InterruptedException e) {
                System.out.println("[에러] " + e.getMessage());
            }
        }
    }
}

 

 

데몬 스레드

1. 일반 스레드 (User Thread)

  • 정의: 일반 스레드는 프로그램의 주요 작업을 수행하며, 프로그램 종료 시까지 실행 상태를 유지합니다. 모든 일반 스레드가 종료되어야 프로그램이 완전히 종료됩니다.
  • 특징:
    • 일반 스레드는 기본적으로 Thread 클래스를 통해 생성
    • 주요 작업을 담당 : 파일 읽기/쓰기, 네트워크 요청 등 프로그램의 핵심 작업을 담당하며, 중요한 역할을 수행하는 스레드
    • 프로그램 종료 여부에 영향 : 일반 스레드가 하나라도 실행 중이면 프로그램은 종료되지 않음.


스레드는 자바에서 작업의 실행 단위로서, 여러 작업을 병렬로 수행할 수 있게 하는 기능입니다. 자바에서는 일반 스레드와 **데몬 스레드(Daemon Thread)**로 스레드를 구분할 수 있으며, 각 스레드는 프로그램의 실행과 종료에 다른 역할을 합니다.

 


2. 데몬 스레드 (Daemon Thread)

  • 정의: 데몬 스레드는 백그라운드에서 보조적인 작업을 수행하며, 일반 스레드가 모두 종료되면 자동으로 종료됩니다. 일반 스레드의 작업을 돕거나, 유지보수에 필요한 작업을 수행하지만, 단독으로 프로그램 종료를 막지는 않습니다.
  • 특징:
    • 백그라운드 작업 : 가비지 컬렉터, 자동 저장, 로그 기록 등 백그라운드에서 계속 실행되다가, 메인 작업이 종료되면 자동으로 종료
    • 자동 종료 : 프로그램의 모든 일반 스레드가 종료되면, 데몬 스레드도 자동으로 종료
    • 설정 방법 : setDaemon(true)메서드를 통해 데몬 스레드로 설정할 수 있습니다. 단, 스레드 시작 전에 설정해야 하며, 시작 후에는 변경할 수 없음.

 

데몬 스레드 예제

// 구구단 스레드 작동 반복의 수가 컴퓨터 성능보다 충분히 크다면 실행 중간에 종료
package com.exam6;

public class ThreadEx {
    public static void main(String[] args) {
        // 스레드를 이용하여 3단과 6단 출력
        Gugudan gugudan1 = new Gugudan(3); // 3단 스레드 생성
        Gugudan gugudan2 = new Gugudan(6); // 6단 스레드 생성

        // 두 스레드를 데몬 스레드로 설정
        gugudan1.setDaemon(true);
        gugudan2.setDaemon(true);

        // 스레드 이름 설정
        gugudan1.setName("3단");
        gugudan2.setName("6단");

        // 스레드 시작
        gugudan1.start();
        gugudan2.start();

        // 메인 스레드가 10밀리초 대기 후 종료
        try {
            Thread.sleep(10); // 메인 스레드가 10ms만 대기
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        // 메인 스레드 종료 후 데몬 스레드들도 자동으로 종료
    }
}

 

중간에 출력이 끊기는 이유

  • 데몬 스레드는 모든 일반 스레드가 종료되면 자동으로 종료됩니다.
  • 이 코드에서는 메인 스레드가 10밀리초만 대기하고 종료되므로, 두 구구단 스레드는 구구단을 모두 출력하기 전에 종료됩니다.
  • 따라서 결과적으로 구구단의 일부만 출력되고, 메인 스레드가 종료되면서 데몬 스레드들도 중단되기 때문에 출력이 중간에 끊기게 됩니다.



'Backend > Java' 카테고리의 다른 글

[Java]java.net 패키지 : InetAddress/URL/URLConnection 클래스  (0) 2024.11.01
공유 자원의 문제와 동기화  (0) 2024.11.01
[Java]Enum(열거형)  (2) 2024.10.31
[Java] 커넥션 풀  (0) 2024.10.31
[Java] 중첩클래스  (0) 2024.10.31