코드는 Runnable 인터페이스를 구현한 MyRunnable 클래스를 생성하고, 이를 실행하는 Thread를 생성하여 동작시킨 후 안전하게 종료하는 방식의 예제입니다.

class MyRunnable implements Runnable {
    /** 
      * volatile 키워드의 역할:
      * volatile을 사용하면 running 변수가 메모리 캐시가 아닌 메인 메모리에서 읽고 쓰도록 보장됩니다.
      * 따라서 한 스레드에서 변경한 running 값을 다른 스레드에서 즉시 감지할 수 있습니다.
      * 이를 통해 while (running) 조건이 변경 사항을 즉시 반영하여 루프가 빠르게 종료될 수 있습니다.
      *    
      * volatile: 변수의 가시성을 보장하지만 원자성은 보장하지 않음.
      * synchronized: 코드 블록에 동기화를 적용해 원자성을 보장하고 경쟁 상태를 방지
      */
    private volatile boolean running = true;

    public void run() {
        while (running) {
            System.out.println("Thread 실행 중...");
            try {
                Thread.sleep(500); // 0.5초 대기
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                break;
            }
        }
        System.out.println("Thread 종료됨.");
    }

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

public class Main{
    public static void main(String[] args) throws InterruptedException {
        MyRunnable myRunnable = new MyRunnable();
        Thread thread = new Thread(myRunnable); // Thread 객체 생성
        thread.start();  // 스레드 시작

        Thread.sleep(2000); // 2초 동안 메인 스레드 대기
        System.out.println("스레드 종료 요청...");
        myRunnable.stopThread(); // 안전하게 종료 요청

        thread.join(); // thread가 종료될 때까지 main 스레드 대기
        System.out.println("메인 스레드 종료.");
    }
}

 

왜 thread.stop()을 사용하지 않고 stopThread()메소드를 만들어 사용할까?

 

 1. 강제 종료로 인한 리소스 정리 문제

  • Thread.stop()은 스레드를 즉시 종료시키므로, 스레드가 사용 중인 리소스 (파일, 네트워크 연결 등)를 정리할 기회를 주지 않습니다.
  • 예를 들어, 파일을 쓰고 있는 도중에 stop()이 호출되면, 파일이 손상될 수 있습니다.

 2. 락(잠금) 해제 문제

  • stop()을 호출하면 스레드가 즉시 종료되므로, 해당 스레드가 점유하고 있던 락(lock)이 즉시 해제됩니다.
  • 이로 인해 다른 스레드가 해당 리소스에 접근할 때 데이터 무결성 문제가 발생할 수 있습니다.
  • 예를 들어, 하나의 스레드가 synchronized 블록 안에서 실행 중인데 stop()이 호출되면, 해당 블록이 비정상적으로 종료되면서 공유 데이터가 손상될 수 있습니다.

 3. ThreadDeath 예외 발생

  • stop()을 호출하면 스레드는 ThreadDeath 예외를 발생시키며 종료됩니다.
  • 이 예외를 잡아 처리하면 스레드가 완전히 종료되지 않고 계속 실행될 수도 있습니다.
  • 예기치 않은 예외로 인해 프로그램이 불안정해질 수 있습니다.

메소드를 만드는 대신 interrupt() 활용 : 

 

스레드를 interrupt()를 이용해 종료하는 것도 좋은 방법입니다. thread.interrupt()를 호출하면 sleep() 상태에서 InterruptedException이 발생하여 catch 블록으로 빠지고, 루프가 종료됩니다.

 

public class Main{
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new MyRunnable());
        thread.start();

        Thread.sleep(2000); // 2초 대기
        System.out.println("스레드 종료 요청...");
        thread.interrupt();  // 인터럽트 요청

        thread.join();
        System.out.println("메인 스레드 종료.");
    }
}

 

 

 

마지막에 선언된 thread.join();의 역할은? 

 

thread.join();은 현재 실행 중인 메인 스레드(main thread)가 thread 스레드가 종료될 때까지 기다리도록 하는 메서드입니다. Java 프로그램에서 여러 개의 스레드를 실행하면, 메인 스레드는 독립적으로 실행됩니다. 즉, 메인 스레드가 먼저 종료되더라도 다른 스레드는 계속 실행될 수 있습니다. 하지만, 특정한 스레드가 완전히 종료된 후에만 다음 작업을 진행하고 싶다면 join()을 사용해야 합니다.

1️⃣ thread.start();로 새 스레드를 실행
2️⃣ main() 스레드는 Thread.sleep(2000);을 실행하며 2초 동안 대기
3️⃣ myRunnable.stopThread();를 호출하여 thread 스레드가 종료하도록 요청
4️⃣ thread.join();을 실행하여 thread가 종료될 때까지 메인 스레드가 대기
5️⃣ thread가 종료된 후 "메인 스레드 종료." 출력

💡 즉, join()을 사용하면 특정 스레드가 종료된 후에 다음 코드가 실행되도록 보장됩니다.

 

핵심:

  • volatile을 사용해 running 변수를 안전하게 공유.
  • stopThread()메서드 활요하여 스레드 종료 요청 / interrupt()로 스레드 종료 요청.
  • join()으로 메인 스레드가 종료될 때까지 대기.

 

'Java' 카테고리의 다른 글

Java ThreadPoolExecutor를 활용한 Custom스레드 풀 구현  (1) 2025.03.08
Java Singleton  (2) 2025.03.01
JAVA Index 기반 문자열 변형 및 랜덤 셔플러  (2) 2025.02.15
비트 연산  (2) 2025.01.31
Java 빌더 패턴 (Builder Pattern)  (0) 2025.01.29

+ Recent posts