Runnable과 Callable 모두 멀티스레드에서 작업을 수행할 수 있게 설계되어있습니다.
Runnable 작업들은 Thread 클래스 또는 ExecutorService 에서 실행할 수 있으나, Callable은 오직 ExecutorService 에서 만 실행할 수 있습니다.
Runnable은 멀티스레드 작업을 표현하기 위해서 자바에서 제공하는 인터페이스 입니다.
@FunctionalInterface
public interface Runnable {
/**
* When an object implementing interface <code>Runnable</code> is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();
}
오직 run()
메서드만 갖고 있고, 어떠한 파라미터도 없으며 어떠한 값도 반환받지 않습니다.
그렇기에 스레드의 결과값이 필요하지 않을때 사용합니다.
예를 들면 아래처럼요.
thread class
@Slf4j
public class EventLoggingTask implements Runnable {
@Override
public void run(){
log.info("MSG");
}
}
run method
public void executeTask(){
ExecutorService executorService = Executors.newFixedThreadPool(2);
executorService.execute(new Thread(EventLoggingTask::new));
executorService.shutdown();
}
위 경우는 단순하게 스레드 내부의 run이 실행될 뿐입니다.
Callable 인터페이스는 generic 값인 V를 반환하는 call()
메서드만 포함하는 인터페이스입니다.
@FunctionalInterface
public interface Callable<V> {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}
위의 Runnable과 다른점이라면 결과값을 계산할 수 없는 경우 Exception을 던진다는 점입니다.
아래의 예시를 통해 이해해봅시다.
thread class
public class FactorialTask implements Callable<Integer> {
int number;
public FactorialTask(int number) {
this.number = number;
}
@Override
public Integer call() throws Exception {
int fact = 1;
for(int count = number; count > 1; count--) {
fact = fact * count;
}
return fact;
}
}
위처럼 스레드 클래스에서 Integer 값을 반환할 수 있게 해두었습니다.
call Method
public void whenTaskSubmitted_ThenFutureResultObtained() throws ExecutionException, InterruptedException {
ExecutorService executorService = Executors.newSingleThreadExecutor();
FactorialTask task = new FactorialTask(5);
Future<Integer> future = executorService.submit(task);
Integer result = future.get();
System.out.println(result);
}
위처럼 사용하면, submit 함과 동시에 스레드 내부의 call 이 실행됩니다.
그리고 이를 Future class를 통해 반환 받은 뒤, get()
메서드를 이용해서 가져올 수 있게 됩니다.
Runnable과 Callable에 대해서 알아보았습니다.
상황에 맞게 잘 사용하면 될 것 같습니다.