study/Java

쓰레드 (Thread)

xoxowo 2023. 1. 2. 14:32

쓰레드 Thread

프로세스의 자원을 이용해서 실제로 작업을 수행하는 것 (쉽게 비유하면 일꾼?)

두 개이상의 쓰레드를 가진 프로세스를 멀티쓰레드 프로세스라고 한다.

실행 중인 사용자 쓰레드(user thread)가 하나도 없을 때 프로그램은 종료된다.

java의 main메서드도 main쓰레드이다.

 

 

쓰레드 구현

1. Thread 클래스를 상속받는법

 Thread 클래스를 상속받으면 다른 클래스를 상속 받을 수 없다. (단일상속)

인스턴스 생성 방법

 Thread의 자손 클래스의 인스턴스를 생성한다.

class MyThread extends Thread {
	public void run() { /* 작업내용 */ } // Thread 클래스의 run()메소드를 오버라이딩
}

class ThreadEx1 {
    public static void main(String[] args) {
        MyThread t1 = new MyThread(); // Thread 자손 클래스의 인스턴스 생성
    }
}

 

2. Runnable 인터페이스를 구현하는 방법  (보통 이 방법을 선택)

인스턴스 생성 방법

Runnable 인터페이스를 구현한 클래스의 인스턴스를 생성한 후 이 인스턴스를 Thread 클래스의 생성자에 매개변수

제공해야한다.

class MyThread implements Runnable {
	public void run() { /*작업내용*/ } // Runnable인터페이스의 run()메소드를 구현
}

class ThreadEx1 {
    public static void main(String[] args) {
        Runnable r = new MyThread(); // 1. Runnable을 구현한 클래스의 인스턴스 생성
        Thread t2 = new Thread(r) // 2. 위에서 만든 인스턴스를 Thread 매개변수로 제공
        
        Thread t2 = new Thread(new MyThread()); // 위 두줄을 한줄로
    }
}

 

쓰레드 구현 예제

class ThreadEx1 {
    public static void main(String[] args) {
        ThreadEx1_1 t1 = new ThreadEx1_1(); // 1번 방법
        
        Runnable r = new ThreadEx1_2(); // 2번 방법
        Thread t2 = new Thread(r); // 생성자 Thread(Runnable target) 매개변수로 넣어준다
//	Thread t2 = new Thread(new ThreadEx1_2()); 위 두줄을 한 줄로

        t1.start(); // 쓰레드 실행하려면 start()를 호출해야함
        t2.start();
    }
}

class ThreadEx1_1 extends Thread {
    public void run() {
        for (int i = 0; i <5; i++) {
            System.out.println(getName()); // 조상인 Thread의 getName()을 호출
        }   // 상속받았기 때문에 this.getName()사용
    }
}

class ThreadEx1_2 implements Runnable {
    public void run() {
        for (int i = 0; i <5; i++) {
            //Thread.currentThread() 현재 실행중인 Thread를 반환
            System.out.println(Thread.currentThread().getName());
        }	// 상속받은게 아니기 때문에 메서드가 getName()이 아님
    }
}

/* 실행 결과
Thread-0
Thread-0
Thread-0
Thread-0
Thread-0
Thread-1
Thread-1
Thread-1
Thread-1
Thread-1
 */

 

start() 

쓰레드를 실행시키기 위해선 start()로 호출해야한다. (예제의 run()은 클래스의 선언된 메서드)

1. main 메서드에서 쓰레드의 start() 호출

2. start()는 새로운 쓰레드가 작업을 실행하는데 필요한 호출 스택(call stack)을 생성한다.

3. 새로 생성된 호출스택에 실행될 메서드가 호출되어, 쓰레드가 독립된 공간에서 작업을 수행한다.

4.호출 스택이 2개 이므로 스케줄러가 정한 순서에 의해 번갈아 가면서 실행된다.

 

 

예제 1

호출 스택의 첫 번째 메서드가 main이 아닌 run 메서드

→ 어떤 쓰레드가 예외 발생하여 종료되어도 다른 쓰레드의 실행에는 영향을 미치지 않는다.

class ThreadEx2 {
    public static void main(String[] args) throws Exception {
        ThreadEx2_1 t1 = new ThreadEx2_1(); // Thread class 상속
        t1.start(); // 쓰레드 실행
    }
}
class ThreadEx2_1 extends Thread {
    public void run() {
        throwException();
    }
    public void throwException() {
        try {
            throw new Exception();
        } catch (Exception e){
            e.printStackTrace(); //printStackTrace 예외가 발생한 호출 스택 출력 
        }
    } 
}

/* 실행 결과
java.lang.Exception
        at ThreadEx2_1.throwException(ThreadEx2.java:13)
        at ThreadEx2_1.run(ThreadEx2.java:9)
 */

 

 

예제 2

쓰레드를 생성하지 않은 예제 단순한 run 메서드 호출이기 때문에 실행 결과에 main메서드가 포함되어있다.

class ThreadEx3 {
    public static void main(String[] args) throws Exception {
        ThreadEx3_1 t1 = new ThreadEx3_1(); // Thread class 상속
        t1.run(); // ThreadEx3_1 클레스의 run() 메서드 실행
    }   // 쓰레드가 실행된게 아님
}
class ThreadEx3_1 extends Thread {
    public void run() {
        throwException();
    }
    public void throwException() {
        try {
            throw new Exception();
        } catch (Exception e){
            e.printStackTrace(); //printStackTrace 예외가 발생한 호출 스택 출력 
        }
    } 
}

/* 실행 결과
java.lang.Exception
        at ThreadEx3_1.throwException(ThreadEx3.java:13)
        at ThreadEx3_1.run(ThreadEx3.java:9)
        at ThreadEx3.main(ThreadEx3.java:4) 
 */