프로그램 실행의 가장 작은 단위
일반적으로 자바 애플리케이션을 실행하면 1개의 main Thread에 의해 프로그램이 실행된다. 하지만 동시에 여러 작업을 처리하고 싶을때는 별도의 쓰레드를 만들어 실행시켜 주어야 하는데, 이때 사용하는 것이 Runnable, Thread 두 방식이 존재한다.
@Test
void runnable() {
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("Thread: " + Thread.currentThread().getName());
}
};
Thread thread = new Thread(runnable);
thread.start();
System.out.println("Hello: " + Thread.currentThread().getName());
}
// 출력 결과
// Hello: main
// Thread: Thread-1
Runnable 인터페이스는 1개의 메소드 만을 갖는 함수형 인터페이스다. 또한 실행을 위해서는 인터페이스의 구현체를 만들어 Thread 객체 생성시에 매개변수로 넘겨주어야 한다.
@Test
void threadStart() {
Thread thread = new MyThread();
thread.start();
System.out.println("Hello: " + Thread.currentThread().getName());
}
static class MyThread extends Thread {
@Override
public void run() {
System.out.println("Thread: " + Thread.currentThread().getName());
}
}
// 출력 결과
// Hello: main
// Thread: Thread-2
Thread는 Runnable 인터페이스를 구현한 클래스로, sleep, interupt, join 등의 메소드을 제공한다. Thread 클래스를 사용하기 위해서는 마찬가지로 Runnable 인터페이스의 run 메소드를 구현하여 호출시 start 메서드를 이용해야 한다.
Runnable은 익명 객체 및 람다로 사요할 수 있지만, Thread는 별도의 클래스를 만들어야 한다는 번거로움이 존재한다. Java에서는 동시 상속이 불가능하므로 Thread 클래스 상속시에는 다른 클래스를 상속받지 못한다는 단점도 존재한다. 또한 Thread 클래스를 상속받으면 클래스에 구현된 코드에 의해 더 많은 자원이 필요로 하므로 Runnable이 주로 사용된다.
이와 같은 문제점 때문에 Java는 Thread 사용 방법을 꾸준히 발전시키고 있고, 그 결과 Executor, ExecutorService, ScheduledExecutionService와 Callable, Future 등이 나오게 되었다.