HOME > java > basic

Java - Runnable과 Callable의 차이점

By JS | 03 Nov 2019

Thread는 Runnable과 Callable의 구현된 함수를 수행한다는 공통점이 있지만, 다음과 같은 차이점이 있습니다.

  • Runnable: 어떤 객체도 리턴하지 않습니다. Exception을 발생시키지 않습니다.
  • Callable: 특정 타입의 객체를 리턴합니다. Exception을 발생킬 수 있습니다.

Runnable

다음은 Runnable 인터페이스 코드입니다. 인자를 받지 않고 리턴값이 없습니다.

public interface Runnable {
    public abstract void run();
}

다음은 Runnable을 사용하는 예제입니다. Thread는 Runnable을 인자로 받고 실행합니다. 어떤 값도 리턴하지 않으며, Runnable의 run() 메소드에 정의된 코드를 수행합니다.

package utils;

import java.time.LocalTime;

public class RunnableExample1 {

    static class MyRunnable implements Runnable {
        @Override
        public void run() {
            String result = "Called at " + LocalTime.now();
            System.out.println(result);
        }
    }

    public static void main(String[] args) {
        MyRunnable runnable = new MyRunnable();
        Thread thread = new Thread(runnable);
        thread.start();
    }
}

결과

Called at 21:13:10.332

위 코드는 다음과 같이 람다식(Lambda)으로 표현할 수 있습니다.

public class RunnableExample2 {

    public static void main(String[] args) {
        Thread thread = new Thread(
            () -> System.out.println("Runnable at " + LocalTime.now()));
        thread.start();
    }
}

Callable

다음은 Callable 인터페이스 코드입니다. 인자를 받지 않으며, 특정 타입의 객체를 리턴합니다. 또한 call() 메소드 수행 중 Exception을 발생시킬 수 있습니다.

public interface Callable<V> {
    V call() throws Exception;
}

다음은 Callable을 사용하는 예제입니다. Thread는 FutureTask를 통해서 Callable을 호출합니다. FutureTask.get()으로 Callable의 call()메소드가 호출되어 결과가 리턴되기를 기다립니다.

package utils;

import java.time.LocalTime;
import java.util.concurrent.*;

public class CallableExample1 {

    static class MyCallable implements Callable<String> {
        @Override
        public String call() throws Exception {
            String result = "Called at " + LocalTime.now();
            return result;
        }
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        MyCallable callable = new MyCallable();
        FutureTask futureTask = new FutureTask(callable);
        Thread thread = new Thread(futureTask);
        thread.start();

        // 결과가 리턴되기를 기다립니다.
        System.out.println("result : " + futureTask.get());
    }
}

결과

result : Called at 21:17:54.051

ExecutorService

ExecutorService도 Callable을 Job으로 등록하고 수행시킬 수 있습니다. 다음 코드처럼 Future를 이용하여 결과를 리턴받을 수 있습니다.

package utils;

import java.time.LocalTime;
import java.util.concurrent.*;

public class CallableExample2 {

    static class MyCallable implements Callable<String> {
        @Override
        public String call() throws Exception {
            String result = "Called at " + LocalTime.now();
            return result;
        }
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        MyCallable callable = new MyCallable();
        ExecutorService executor = Executors.newSingleThreadExecutor();
        Future<String> future = executor.submit(callable);

        // 결과가 리턴되기를 기다립니다.
        System.out.println("result : " + future.get());
    }
}

결과

result : Called at 21:19:25.693

위 코드도 다음처럼 람다식(Lambda)으로 표현할 수 있습니다.

public class CallableExample3 {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        Future<String> future = executor.submit(() -> "Called at " + LocalTime.now());

        // 결과가 리턴되기를 기다립니다.
        System.out.println("result : " + future.get());
    }
}