Runnable와 Callable은 Java의 멀티스레딩 프로그래밍에서 사용되는 인터페이스로, 비동기 작업을 정의하는데 사용됩니다. 두 인터페이스는 비슷한 역할을 하지만, 중요한 차이점이 있어 특정 상황에 따라 더 적합하게 사용될 수 있습니다.
Runnable vs Callable 인터페이스 비교
1. 기본 개념과 동작 방식
- Runnable: Runnable은 Java 1.0부터 제공되는 인터페이스로, 실행할 작업을 정의할 수 있습니다. Runnable을 구현한 클래스는 run() 메서드를 오버라이드해야 합니다. 이 메서드는 반환값이 없고(void), 주로 간단한 비동기 작업이나 스레드로 실행할 작업을 정의할 때 사용됩니다.
- Callable: Callable은 Java 5에서 추가된 인터페이스로, Runnable과 비슷하게 비동기 작업을 정의하지만, 반환값을 가질 수 있다는 점이 다릅니다. Callable을 구현한 클래스는 call() 메서드를 오버라이드하며, 이 메서드는 제네릭 타입 T의 값을 반환하고, 예외를 던질 수 있는 기능을 가지고 있습니다.
2. 반환값 유무 및 활용
- Runnable은 반환값이 없어 주로 단순한 작업에 적합하며, Thread나 ExecutorService를 사용해 실행할 수 있습니다.
Runnable task = () -> System.out.println("Runnable Task");
new Thread(task).start();
- Callable은 반환값이 필요한 작업에 적합하며, 결과를 얻기 위해 ExecutorService의 submit() 메서드를 사용하여 Future 객체를 활용합니다.
Callable<String> task = () -> "Callable Task Result";
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<String> future = executor.submit(task);
String result = future.get();
3. 예외 처리
- Runnable의 run() 메서드는 예외를 던질 수 없습니다. 따라서 예외가 발생할 경우, 반드시 try-catch로 처리해야 합니다.
Runnable task = () -> {
try {
System.out.println("Runnable Task");
} catch (Exception e) {
e.printStackTrace();
}
};
- Callable은 call() 메서드가 Exception을 던질 수 있어 예외 처리가 필요할 때 더 유연하게 사용할 수 있습니다. 예외는 Future.get() 호출 시 확인 가능합니다.
Callable<String> task = () -> {
if (true) throw new Exception("Callable Exception");
return "Callable Task Result";
};
4. 스레드 풀과 Future의 사용
- Runnable은 주로 Thread나 ExecutorService에서 실행되며, 결과를 기다리지 않거나 필요 없는 경우에 주로 사용됩니다.
- Callable은 ExecutorService와 함께 사용되어 submit() 메서드로 실행하고, 반환된 Future 객체로 결과를 얻거나 작업의 상태를 모니터링할 수 있습니다.
결론
- Runnable은 단순하고 반환값이 필요 없는 작업에 적합하며, 예외 처리가 제한적입니다.
- Callable은 결과가 필요하거나 예외 처리가 필요한 비동기 작업에 유리하며, Future와 결합하여 유연하게 사용할 수 있습니다.