오늘은 제목에 있는 것 처럼, Thread(쓰레드인지, 스레드인지...스레드로 쓰겠음)를 이야기해 볼까 한다.

스레드는 프로세스 내에서 실해오디는 독립적인 작업 단위이다. 프로세스는 실행 중인 프로그램을 나타내며, 하나 이상의 스레드로 구성될 수 있다. 각 스레드는 독립적인 실행 흐름을 가지며, 동시에 여러 스레드가 실행될 수 있다.
쉽게 풀어보자, 일단 "막내가 회의를 위해 해야하는 일"이라는 "프로세스"가 있다고 상상해보자. 그럼 막내는 회의를 위해서, "회의 자료 출력", "회의실 세팅" 등등의 작업을 해야한다 여기서 "회의 자료 출력", "회의실 세팅" 등등의 작업이 "스레드"이다.
자바에서 스레드를 구현하는 방법은 다양하다. 대표적인 예들만 이야기 해보겠다.
class MyThread extends Thread {
public void run() {
// 스레드의 실행 코드 작성
System.out.println("스레드 실행");
}
}
// 스레드 객체 생성
MyThread thread = new MyThread();
thread.start(); // 스레드 실행
Thread 클래스를 상속받아서 스레드를 구현하는 방법은 가장 직관적이고 간단한 방법이다. 이 방법을 사용하려면 Thread 클래스를 상속받은 후 run() 메서드를 오버라이드하여 스레드의 실행 코드를 작성한다. 그리고 start() 메서드를 호출하여 스레드를 실행한다.
class MyRunnable implements Runnable {
public void run() {
// 스레드의 실행 코드 작성
System.out.println("스레드 실행");
}
}
// 스레드 객체 생성
Thread thread = new Thread(new MyRunnable());
thread.start(); // 스레드 실행
Runnable 인터페이스를 구현하는 방법은 객체 지향적인 설계를 장려하고 코드 재사용성을 높일 수 있는 방법이다. 이 방법을 사용하려면 Runnable 인터페이스를 구현한 후 run() 메서드를 정의하여 스레드의 실행 코드를 작성한다. 그리고 Runnable 객체를 Thread 클래스의 생성자에 전달하여 스레드를 생성하고, start() 메서드를 호출하여 스레드를 실행한다.
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
class MyCallable implements Callable<String> {
public String call() throws Exception {
// 스레드의 실행 코드 작성
return "스레드 실행";
}
}
// 스레드 객체 생성
FutureTask<String> futureTask = new FutureTask<>(new MyCallable());
Thread thread = new Thread(futureTask);
thread.start(); // 스레드 실행
// 결과 가져오기
String result = futureTask.get();
System.out.println(result);
Java 5부터 도입된 Callable 인터페이스와 Future 클래스를 사용하여 스레드를 구현할 수 있다. Callable은 Runnable과 유사하지만 실행 결과를 반환할 수 있는 call() 메서드를 정의한다. Future 클래스는 Callable이 생성한 결과를 비동기적으로 가져올 수 있도록 지원한다.
// Runnable 인터페이스를 람다식으로 구현
Runnable runnable = () -> {
// 스레드의 실행 코드 작성
System.out.println("스레드 실행");
};
// 스레드 객체 생성
Thread thread = new Thread(runnable);
thread.start(); // 스레드 실행
Java 8부터는 람다식과 함수형 인터페이스를 활용하여 스레드를 구현할 수 있다. Runnable과 Callable은 함수형 인터페이스로 간주될 수 있으므로, 람다식을 사용하여 스레드의 실행 코드를 간결하게 작성할 수 있다.
이 외에도 Java에서는 스레드 풀(Thread Pool)을 사용하는 방법이나 Executor 프레임워크를 활용하는 방법 등 다양한 방법을 제공한다.
가장 보편적으로 쓰이는 방식은 Runnable 인터페이스를 사용하는 것이다. Runnable 인터페이스가 많이 활용되는 이유는,
객체 지향적인 설계
Runnable 인터페이스는 함수형 인터페이스로, run() 메서드 하나만을 가지고 있다. 이는 객체 지향 프로그래밍의 원칙에 부합하며, 스레드 실행 코드를 단일 메서드로 캡슐화하여 관리할 수 있습니다.
코드 재사용성
Runnable 인터페이스를 구현하는 클래스는 다른 클래스에서도 사용될 수 있다. 스레드의 실행 코드를 Runnable 객체로 추상화하면, 동일한 실행 코드를 다른 스레드나 실행 컨텍스트에서 재사용할 수 있다.
유연한 스레드 관리
Runnable 객체는 스레드 생성과 실행을 분리할 수 있다. Runnable 객체를 생성한 후, 이를 Thread 클래스의 생성자에 전달하여 스레드를 생성하고 실행할 수 있다. 이는 실행 코드와 스레드 관리를 분리하여 유연하게 작업을 처리할 수 있게 한다.