본문 바로가기

프로그래밍언어/JAVA

[JAVA] 쓰레드 구현 및 실행 (Thread, Runnable, start(), run())

반응형

1. 쓰레드 구현

 

쓰레드를 구현하는 방법은 Thread 클래스를 상속받는 방법과 Runnable 인터페이스를 구현하는 방법, 두가지가 있다. 각각의 방식으로 구현은 다음과 같이 할 수 있다.

 

// Thread 클래스 상속
class ThreadClass extends Thread {
    // Thread 클래스의 run() 을 오버라이딩
    public void run() {
	    ...
    }
}

// Runnable 인터페이스 구현
class RunnableClass implements Runnable {
    // Runnable 인터페이스의 추상메서드 run() 을 구현
    public void run() {
	    ...
    }
}

 

둘 간의 큰 차이는 없지만 Thread 클래스를 상속받으면 다른 클래스를 상속받을 수 없기 때문에 Runnable 인터페이스를 구현하는 방법이 일반적이다.

Runnable 인터페이스를 구현하는 방법은 재사용성이 높고 코드의 일관성을 유지할 수 있기 때문에 보다 객체지향적인 방법이다. Runnable 인터페이스는 오로지 run() 만 정의되어 있는 간단한 인터페이스이다.

쓰레드를 구현한다는 것은 두 방법 중 어떤 것을 사용하든지, 그저 쓰레드를 통해 작업하고자 하는 내용으로 run() 의 바디를 채우는 것일 뿐이다.

Thread 클래스를 상속받는 경우와 Runnable 인터페이스를 구현한 경우의 인스턴스 생성 방법이 다르다.

 

ThreadClass t1 = new TreadClass(); // Thread 를 상속받는 ThreadClass 클래스의 인스턴스 생성

Thread t2 = new Thread(new RunnableClass()); // Runnable 을 구현하는 RunnableClass 클래스의 인스턴스를 Thread 클래스 생성자에 매개변수로 넣어준다.


Thread 클래스에서는 매개변수로 받은 Runnable 객체를 참조변수에 저장시켜서 사용한다. 이를통해 따로 run() 메서드를 구현하지 않고도 Runnable 객체를 통해서 외부에서 run() 메서드를 받아서 사용할 수 있다.

또한 현재 쓰레드의 이름을 받아오는 부분에도 차이가 있다. Thread 를 상속받은 경우에는 getName() 메서드를 사용하면 현재 쓰레드의 이름을 구할 수 있다. 하지만 Runnable 인터페이스를 구현한 경우에는 Thread.currentThread().getName() 과 같이 해야한다.

쓰레드의 이름은 생성자나 setName() 메서드를 사용해서 지정 또는 변경할 수 있다. 따로 쓰레드의 이름을 지정하지 않으면 Thread-번호 의 형식으로 이름이 지정된다.

 

 

2. 쓰레드 실행

 

- start()

쓰레드를 생성한 후에는 start() 를 호출하여 쓰레드를 실행시켜야 한다.

 

t1.start();
t2.start();


start() 메서드를 호출하면 각 쓰레드가 실행 대기 상태에 들어간다. 이후 자신의 차례가 되면 실행이 된다.

쓰레드의 start() 는 한번만 호출할 수 있다. 다시말해 한번 실행이 종료된 쓰레드는 다시 실행할 수 없다는 의미이다. 만약 두번 이상 start() 를 호출하는 경우에는 IllegalThreadStateException 이 발생하게 된다. 그렇기 때문에 동일한 작업을 여러번 수행하기 위해서는 매번 새로운 쓰레드를 생성하여 실행시켜야 한다.

 

 

- start() vs run()

 

쓰레드를 실행시킬 때 run() 이 아닌 start() 를 호출한다. main 메서드에서 run() 을 호출하는 것은 생성된 쓰레드를 실행시키는 것이 아니라 단순히 클래스에 선언된 메서드를 호출하는 것일 뿐이다. 반면에 start() 메서드를 호출하는 것은 새로운 쓰레드가 작업을 실행하는데 필요한 호출 스택 (call stack) 을 생성한 다음에 run() 을 호출해서 생성된 호출스택에 run() 이 첫번째로 올라가게 한다.

모든 쓰레드는 독립적인 작업을 수행하기 위해 자신만의 호출스택을 필요로 한다. 그렇기 때문에 새로운 쓰레드를 생성하고 실행시킬 때마다 새로운 호출스택이 생성되고 쓰레드가 종료되면 작업에 사용된 호출스택은 소멸된다.

 

쓰레드 실행 순서

1) main 메서드에서 쓰레드의 start() 를 호출한다.
2) start() 는 새로운 쓰레드를 생성하고 쓰레드가 작업하는데 사용될 호출스택을 생성한다.
3) 새로 생성된 호출스택에 run() 이 호출되어 쓰레드가 독립된 공간에서 작업을 수행한다.
4) 2개의 호출스택이 각각 스케줄러가 정한 순서에 의해 번갈아 가면서 실행된다.

 

멀티쓰레딩 프로그램에서 각 쓰레드들은 스케줄러에 따라 작업을 수행한다. 스케줄러는 정해진 정책에 따라 순서와 일정 시간을 각 쓰레드에 할당하는데, 해당 시간동안 작업을 마치지 못한 쓰레드는 다시 자신의 차례가 올 때까지 대기한다.

작업을 마친 쓰레드는 호출 스택이 모두 비워지면서 이 쓰레드가 사용하던 호출스택은 사라진다.

 

 

3. main 쓰레드

자바 프로그램에서 main 메서드를 수행하는 것도 쓰레드이며, 이를 main 쓰레드라고  한다. 기본적으로 main 메서드가 수행을 마치면 프로그램이 종료되었으나, 다른 쓰레드의 작업이 종료되지 않은 경우에는 프로그램이 종료되지 않는다.
쓰레드는 사용자 쓰레드와 데몬 쓰레드, 두 종류로 구분된다.

반응형

'프로그래밍언어 > JAVA' 카테고리의 다른 글

[JAVA] 쓰레드의 동기화  (0) 2022.03.15
[JAVA] 쓰레드 실행제어  (0) 2022.03.11
[JAVA] 프로세스와 쓰레드  (1) 2022.03.08
[JAVA] Annotation  (0) 2022.02.23
[JAVA] Enums  (0) 2022.02.21