Java에서 Runnable 인터페이스와 Callable 인터페이스의 차이점


답변:


444

여기 설명을 참조 하십시오 .

Callable 인터페이스는 Runnable과 유사합니다. 둘 다 인스턴스가 다른 스레드에 의해 잠재적으로 실행될 수있는 클래스를 위해 설계되었습니다. 그러나 Runnable은 결과를 반환하지 않으며 확인 된 예외를 throw 할 수 없습니다.


269

어떤이의 애플리케이션에서 차이가있다 RunnableCallable. 에 반환 매개 변수와의 차이점 만 Callable있습니까?

기본적으로 그렇습니다. 이 질문에 대한 답변을 참조하십시오 . 에 대한 javadocCallable .

Callable모든 것을 할 수 있다면 둘 다 가질 필요는 무엇입니까 Runnable?

Runnable인터페이스 가 모든 것을 할 수 없기 때문에 Callable!

RunnableJava 1.0 이후로 사용되었지만 CallableJava 1.5에서만 소개되었습니다 ... Runnable지원하지 않는 사용 사례를 처리하기 위해. 이론적으로 Java 팀은 Runnable.run()메소드 의 서명을 변경했을 수 있지만 이는 1.5 이전 코드와 바이너리 호환성을 손상시켜 이전 Java 코드를 최신 JVM으로 마이그레이션 할 때 코드를 다시 작성해야합니다. 그것은 큰 NO-NO입니다. Java는 이전 버전과의 호환성을 위해 노력하고 있으며 이는 비즈니스 컴퓨팅을위한 Java의 가장 큰 판매 지점 중 하나입니다.

그리고 분명히 작업이 결과를 반환하거나 확인 된 예외를 throw 할 필요 가없는 사용 사례가 있습니다 . 이러한 유스 케이스의 경우 사용 Runnable은 메소드 에서 Callable<Void>더미 ( null) 값을 사용 하고 리턴하는 것보다 더 간결 합니다 call().


9
이 역사를 어디서 얻었는지 궁금합니다. 이것은 매우 유용합니다.
스파이더 맨

4
@prash-기본 사실은 오래된 교과서에서 찾을 수 있습니다. 간단히 말해서 Java의 첫 번째 버전과 같습니다.
Stephen C

4
(@prash-또한 Java 1.1 시대에 Java를 사용하기 시작함으로써.)
Stephen C

1
@StephenC 대답을 올바르게 읽으면 Runnable이전 버전과의 호환성을 위해 (대부분) 존재한다고 제안합니다 . 그러나 Callable인터페이스 를 구현 (또는 요구)하는 데 불필요하거나 너무 비싼 상황은 ScheduledFuture<?> ScheduledExecutorService.schedule(Runnable command, long delay, TimeUnit unit)없습니까? 그렇다면 역사가 현재의 결과를 강요하지 않더라도 언어로 두 인터페이스를 유지하는 데 이점이 있습니까?
최대

1
@ max-글쎄, 나는 여전히 그것에 동의합니다. 그러나 이것이 두 번째 이유입니다. 그러나 그럼에도 불구 하고 호환성을 유지해야 할 의무가 없다면 수정 Runnable 되었을 것 입니다. "boilerplate" return null;는 약한 주장입니다. (적어도, 그것은 내 결정이었을 것이다. 당신이 이전 버전과의 호환성을 무시할 수있는 가설 적 맥락에서.)
Stephen C

82
  • A는 Callable구현해야 call()잠시 방법을 Runnable요구 구현하는 run()방법을.
  • A Callable는 값을 반환 할 수 있지만 a는 반환 Runnable할 수 없습니다.
  • A Callable는 검사 예외를 던질 수 있지만 Runnable불가능합니다.
  • A CallableExecutorService#invokeXXX(Collection<? extends Callable<T>> tasks)메소드 와 함께 사용할 수 있지만 a Runnable는 사용할 수 없습니다.

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

17
ExecutorService.submit (Runnable task)도 존재하며 매우 유용합니다
Yair Kukielka

Runnable은 다음과 같은 방법으로 ExecutorService와 함께 사용할 수도 있습니다. 1) ExecutorService.execute (Runnable) 2) ExecutorService.submit (Runnable)
Azam Khan

2
또한 Executor.submit (Callable <T> task)이 있지만 Runnable tasks Collection을 사용하여 invokeAll 또는 invokeAny를 호출 할 수 없습니다. Callable <T >> 작업을 확장
nikli

36

나는 이것을 다른 블로그 에서이 차이점을 조금 더 설명 할 수있는 것을 발견했다 .

두 인터페이스는 다른 실행 스레드에서 실행하려는 클래스에 의해 구현되지만 두 인터페이스 사이에는 다음과 같은 차이점이 거의 없습니다.

  • Callable<V>인스턴스 유형의 결과를 반환 V반면, Runnable인스턴스하지 않습니다.
  • Callable<V>인스턴스는 반면, 체크 된 예외를 던질 수 Runnable인스턴스가 없습니다

Java 설계자는 Runnable인터페이스 의 기능을 확장해야 할 필요성을 느꼈지만 인터페이스 사용에 영향을 미치고 싶지 Runnable않았을 수도 있습니다. 아마도 Callable이미 Java 1.5에서 별도의 인터페이스를 사용하는 이유 는 이미 기존 Runnable.


27

Runnable과 Callable을 사용할 위치를 살펴 보겠습니다.

Runnable과 Callable은 모두 호출 스레드와 다른 스레드에서 실행됩니다. 그러나 Callable은 값을 반환하고 Runnable은 값을 반환 할 수 없습니다. 그래서 이것이 실제로 어디에 적용됩니까?

Runnable : 화재가 있고 작업을 잊어 버린 경우 Runnable을 사용하십시오. Runnable 안에 코드를 넣고 run () 메서드가 호출되면 작업을 수행 할 수 있습니다. 호출 스레드는 실제로 작업을 수행 할 때 중요하지 않습니다.

호출 가능 : 작업에서 값을 검색하려는 경우 호출 가능을 사용하십시오. 이제 스스로 호출 할 수는 없다. Callable을 감싸고 future.get ()에서 가치를 얻는 미래가 필요합니다. 여기서 호출 스레드는 Future가 결과로 돌아올 때까지 차단되며 결과적으로 Callable의 call () 메소드가 실행될 때까지 대기합니다.

따라서 Runnable 및 Callable 랩핑 메소드가 모두 정의 된 대상 클래스에 대한 인터페이스를 고려하십시오. 호출 클래스는 Runnable과 Callable을 모르는 인터페이스 메소드를 무작위로 호출합니다. Runnable 메소드는 Callable 메소드가 호출 될 때까지 비동기식으로 실행됩니다. 대상 클래스에서 값을 검색하기 때문에 호출 클래스의 스레드가 차단됩니다.

참고 : 대상 클래스 내에서 단일 스레드 실행기에서 Callable 및 Runnable을 호출 하여이 메커니즘을 직렬 디스패치 큐와 유사하게 만들 수 있습니다. 호출자가 Runnable 랩핑 된 메소드를 호출하는 한 호출 스레드는 차단하지 않고 실제로 빠르게 실행됩니다. Future 메소드에 Callable 랩핑 된 Callable을 호출하자마자 대기중인 다른 모든 항목이 실행될 때까지 차단해야합니다. 그래야만 메소드가 값과 함께 리턴됩니다. 이것은 동기화 메커니즘입니다.


14

Callable인터페이스는 call()메소드를 선언 하고 Object call () 유형이 반환해야하므로 제네릭을 제공해야합니다.

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반면 run()runnable을 사용하여 Thread를 작성하고 start ()를 호출 할 때 호출되는 메소드 를 선언 하는 인터페이스가 있습니다. 직접 run ()을 호출 할 수도 있지만 run () 메서드를 실행하는 것은 동일한 스레드입니다.

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();
}

몇 가지 주목할만한 차이점을 요약하면

  1. Runnable반면 객체는 결과를 반환하지 않는 Callable개체가 결과를 반환합니다.
  2. Runnable개체가 예외를 throw 할 수 있는 동안 개체는 확인 된 예외를 throw 할 수 없습니다 Callable.
  3. Runnable반면 인터페이스는 자바 1.0 이후 주변왔다 Callable에만 자바 1.5에서 소개되었습니다.

몇 가지 유사점 포함

  1. Runnable 또는 Callable 인터페이스를 구현하는 클래스의 인스턴스는 다른 스레드에 의해 잠재적으로 실행될 수 있습니다.
  2. Callable 및 Runnable 인터페이스의 인스턴스는 submit () 메소드를 통해 ExecutorService에 의해 실행될 수 있습니다.
  3. 둘 다 기능적 인터페이스이며 Java8 이후 Lambda 표현식에서 사용할 수 있습니다.

ExecutorService 인터페이스의 메소드는 다음과 같습니다.

<T> Future<T> submit(Callable<T> task);
Future<?> submit(Runnable task);
<T> Future<T> submit(Runnable task, T result);

13

오라클 문서에서 이러한 인터페이스의 목적 :

실행 가능한 인터페이스는 인스턴스를 a로 실행하려는 모든 클래스에서 구현해야합니다 Thread. 클래스는라는 인수가없는 메소드를 정의해야합니다 run.

호출 가능 : 결과를 반환하고 예외가 발생할 수있는 작업입니다. 구현자는 call이라는 인수가없는 단일 메소드를 정의합니다. 이 Callable인터페이스는 Runnable인스턴스가 다른 스레드에 의해 잠재적으로 실행될 수있는 클래스를 위해 설계되었다는 점에서 와 비슷합니다 . Runnable그러나 A 는 결과를 반환하지 않으며 확인 된 예외를 throw 할 수 없습니다.

다른 차이점들 :

  1. ThreadRunnable 를 생성하도록 전달할 수 있습니다 . 그러나 매개 변수 로 전달하여 새 스레드를 만들 수는 없습니다 . Callable을 인스턴스 에만 전달할 수 있습니다 .CallableExecutorService

    예:

    public class HelloRunnable implements Runnable {
    
        public void run() {
            System.out.println("Hello from a thread!");
        }   
    
        public static void main(String args[]) {
            (new Thread(new HelloRunnable())).start();
        }
    
    }
  2. Runnable화재에 사용 하고 전화를 잊어 버립니다. 사용 Callable결과를 확인 할 수 있습니다.

  3. Callable와 달리 invokeAll 메소드에 전달할 수 있습니다 Runnable. 가장 일반적으로 유용한 대량 실행 방법, 작업 모음을 실행 한 후 하나 이상이 완료되기를 기다리는 방법 invokeAnyinvokeAll수행

  4. 사소한 차이 : 구현할 메소드 이름 => run()for Runnableand call()for Callable.


11

이미 언급했듯이 Callable은 비교적 새로운 인터페이스이며 동시성 패키지의 일부로 도입되었습니다. Callable 및 Runnable은 모두 실행 프로그램과 함께 사용할 수 있습니다. Runnable 자체를 구현하는 클래스 스레드는 Runnable 만 지원합니다.

실행기와 함께 Runnable을 계속 사용할 수 있습니다. Callable의 장점은 executor로 전송하여 즉시 완료 할 수 있다는 것입니다. 실행이 완료되면 업데이트 될 미래 결과. Runnable에서도 동일하게 구현 될 수 있지만이 경우 결과를 직접 관리해야합니다. 예를 들어 모든 결과를 보유 할 결과 대기열을 생성 할 수 있습니다. 다른 스레드는이 큐에서 대기하고 도착한 결과를 처리 할 수 ​​있습니다.


Java에서 예외를 발생시키는 스레드의 예가 무엇인지 궁금합니다. 메인 스레드가 해당 예외를 잡을 수 있습니까? 그렇지 않으면 Callable을 사용하지 않습니다. 알렉스, 이것에 대한 통찰력이 있습니까? 감사!

1
다른 코드와 같이 사용자 지정 스레드에서 실행되는 코드는 예외를 throw 할 수 있습니다. 다른 스레드에서 잡으려면 사용자 정의 알림 메커니즘 (예 : 리스너 기반)을 사용 Future하거나 모든 미 발견 예외를 포착하는 후크를 사용 하거나 추가 하여 몇 가지 노력을 수행해야합니다 . docs.oracle.com/javase/6/docs/api/ java / lang /…
AlexR

좋은 정보! 고마워, 알렉스! :)

1
이 대답은 (정확히 액면 값을 취한 경우) 호출 가능한 객체와 함께 스레드 풀 모델을 사용해야한다고 주장하기 때문에이 답변을 찬성했습니다. 이것에 대해 분명히 불행한 것은 하나의 스레드가 호출 가능한 것들과 개발자가 원하는 다른 것들을 수행하도록 사용자 정의 할 수 있도록 인터페이스를 Thread의미있게 사용할 수 없다는 Callable것입니다. 이 의견을 읽는 사람이 내가 틀렸다고 생각하면, 더 잘 알고 싶습니다.

8
+-------------------------------------+--------------------------------------------------------------------------------------------------+
|              Runnable               |                                           Callable<T>                                            |
+-------------------------------------+--------------------------------------------------------------------------------------------------+
| Introduced in Java 1.0 of java.lang | Introduced in Java 1.5 of java.util.concurrent library                                           |
| Runnable cannot be parametrized     | Callable is a parametrized type whose type parameter indicates the return type of its run method |
| Runnable has run() method           | Callable has call() method                                                                       |
| Runnable.run() returns void         | Callable.call() returns a value of Type T                                                        |
| Can not throw Checked Exceptions    | Can throw Checked Exceptions                                                                     |
+-------------------------------------+--------------------------------------------------------------------------------------------------+

Java 설계자는 Runnable인터페이스 의 기능을 확장해야 할 필요성을 느꼈지만 인터페이스 사용에 영향을 미치고 싶지 않았기 때문에 Runnable아마도 별도의 인터페이스라는 이름을 가진 이유 일 것입니다.Callable 이미 Java 1.5에서 는 이미 RunnableJava 1.0 이후 Java의 일부인 기존 인터페이스 출처


7

Callable과 Runnable의 차이점은 다음과 같습니다.

  1. Callable은 JDK 5.0에 도입되었지만 Runnable은 JDK 1.0에 도입되었습니다
  2. Callable에는 call () 메서드가 있지만 Runnable에는 run () 메서드가 있습니다.
  3. Callable에는 값을 반환하는 호출 메서드가 있지만 Runnable에는 값을 반환하지 않는 run 메서드가 있습니다.
  4. 호출 메소드는 확인 된 예외를 던질 수 있지만 실행 메소드는 확인 된 예외를 던질 수 없습니다.
  5. 작업 대기열에 넣으려면 호출 가능한 submit () 메소드를 사용하고 태스크 대기열에 실행하려면 Runnable 사용 execute () 메소드를 사용하십시오.

RuntimeException이 아닌 체크 된 예외 를 강조하는 것이 중요합니다.
BertKing

5

호출 가능 Runnable 은 서로 비슷하며 스레드 구현에 사용할 수 있습니다. 구현하는 경우 의 Runnable을 당신은 구현해야 실행 () 메소드 만 호출의 경우 구현해야한다 ()를 호출 방법을 비슷한 방식으로 만 호출 두 방법 작업 호출 () 방법이 더 flexibility.There는 그들 사이에 약간의 차이가 있습니다.

아래와 같이 RunnableCallable의 차이점

1) runnablerun () 메소드 는 void를 반환 합니다. 스레드가 더 사용할 수있는 것을 반환하려면 Runnable run () 메소드를 선택할없습니다 . 해결책이있다'Callable' 객체 형태로 물건을 반환 하려면 Runnable 대신 Callable사용해야합니다 . 호출 가능한 인터페이스에는 Object를 반환하는 'call ()' 메서드가 있습니다. .

메소드 서명-실행 가능->

public void run(){}

호출 가능->

public Object call(){}

2) Runnable run () 메소드의 경우 검사 된 예외가 발생하면 try catch block으로 처리 해야 하지만 Callable call ()의 경우 메소드의 경우 다음 과 같이 검사 된 예외throw 있습니다

 public Object call() throws Exception {}

3) 의 Runnable는 기존의에서 오는 자바 1.0 버전하지만, 호출이 와서 Executer 와 함께 Java 1.5 버전으로 제공 프레임 워크 됩니다.

익숙하다면 Executers Runnable 대신 Callable 을 사용해야 합니다. .

이해하시기 바랍니다.


2

실행 가능 (vs) 호출 가능 Executer 프레임 워크를 사용할 때 이 시작됩니다.

ExecutorService는 ExecutorRunnable 및 Callable 작업을 모두 허용 하는의 하위 인터페이스입니다 .

초기 멀티 스레딩은 Interface 1.0 이후 인터페이스를 사용하여 달성 할 수 있지만 여기서는 스레드 작업을 완료 한 후 스레드 정보를 수집 할 수 없습니다. 데이터를 수집하기 위해 정적 필드를 사용할 수 있습니다.Runnable

예 각 학생 데이터를 수집하기 위해 별도의 스레드.

static HashMap<String, List> multiTasksData = new HashMap();
public static void main(String[] args) {
    Thread t1 = new Thread( new RunnableImpl(1), "T1" );
    Thread t2 = new Thread( new RunnableImpl(2), "T2" );
    Thread t3 = new Thread( new RunnableImpl(3), "T3" );

    multiTasksData.put("T1", new ArrayList() ); // later get the value and update it.
    multiTasksData.put("T2", new ArrayList() );
    multiTasksData.put("T3", new ArrayList() );
}

이 문제를 해결하기 위해 1.5 를 도입 하여 결과를 반환하고 예외를 throw 할 수 있습니다.Callable<V>

  • 단일 추상 메소드 : Callable 인터페이스와 Runnable 인터페이스에는 단일 추상 메소드가 있으므로 Java 8의 람다 식에 사용할 수 있습니다.

    public interface Runnable {
    public void run();
    }
    
    public interface Callable<Object> {
        public Object call() throws Exception;
    }

실행 태스크를 ExecutorService 에 위임하는 방법에는 몇 가지가 있습니다.

  • execute(Runnable task):void 이 메소드는 void를 리턴하므로 새 스레드를 크레이트하지만 기본 스레드 또는 호출자 스레드를 차단하지 않습니다.
  • submit(Callable<?>):Future<?>, submit(Runnable):Future<?>당신이 사용하는 경우 새 스레드와 블록 메인 스레드를 상자 하는 Future.get () .

실행기 프레임 워크에서 호출 가능, 호출 가능 인터페이스를 사용하는 예.

class CallableTask implements Callable<Integer> {
    private int num = 0;
    public CallableTask(int num) {
        this.num = num;
    }
    @Override
    public Integer call() throws Exception {
        String threadName = Thread.currentThread().getName();
        System.out.println(threadName + " : Started Task...");

        for (int i = 0; i < 5; i++) {
            System.out.println(i + " : " + threadName + " : " + num);
            num = num + i;
            MainThread_Wait_TillWorkerThreadsComplete.sleep(1);
        }
        System.out.println(threadName + " : Completed Task. Final Value : "+ num);

        return num;
    }
}
class RunnableTask implements Runnable {
    private int num = 0;
    public RunnableTask(int num) {
        this.num = num;
    }
    @Override
    public void run() {
        String threadName = Thread.currentThread().getName();
        System.out.println(threadName + " : Started Task...");

        for (int i = 0; i < 5; i++) {
            System.out.println(i + " : " + threadName + " : " + num);
            num = num + i;
            MainThread_Wait_TillWorkerThreadsComplete.sleep(1);
        }
        System.out.println(threadName + " : Completed Task. Final Value : "+ num);
    }
}
public class MainThread_Wait_TillWorkerThreadsComplete {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        System.out.println("Main Thread start...");
        Instant start = java.time.Instant.now();

        runnableThreads();
        callableThreads();

        Instant end = java.time.Instant.now();
        Duration between = java.time.Duration.between(start, end);
        System.out.format("Time taken : %02d:%02d.%04d \n", between.toMinutes(), between.getSeconds(), between.toMillis()); 

        System.out.println("Main Thread completed...");
    }
    public static void runnableThreads() throws InterruptedException, ExecutionException {
        ExecutorService executor = Executors.newFixedThreadPool(4);
        Future<?> f1 = executor.submit( new RunnableTask(5) );
        Future<?> f2 = executor.submit( new RunnableTask(2) );
        Future<?> f3 = executor.submit( new RunnableTask(1) );

        // Waits until pool-thread complete, return null upon successful completion.
        System.out.println("F1 : "+ f1.get());
        System.out.println("F2 : "+ f2.get());
        System.out.println("F3 : "+ f3.get());

        executor.shutdown();
    }
    public static void callableThreads() throws InterruptedException, ExecutionException {
        ExecutorService executor = Executors.newFixedThreadPool(4);
        Future<Integer> f1 = executor.submit( new CallableTask(5) );
        Future<Integer> f2 = executor.submit( new CallableTask(2) );
        Future<Integer> f3 = executor.submit( new CallableTask(1) );

        // Waits until pool-thread complete, returns the result.
        System.out.println("F1 : "+ f1.get());
        System.out.println("F2 : "+ f2.get());
        System.out.println("F3 : "+ f3.get());

        executor.shutdown();
    }
}

0

함수형 프로그래밍과 일치하는 일종의 인터페이스 명명 규칙입니다.

//Runnable
interface Runnable {
    void run();
}

//Action - throws exception
interface Action {
    void run() throws Exception;
}

//Consumer - consumes a value/values, throws exception
interface Consumer1<T> {
    void accept(T t) throws Exception;
}

//Callable - return result, throws exception
interface Callable<R> {
    R call() throws Exception;
}

//Supplier - returns result, throws exception
interface Supplier<R> {
    R get() throws Exception;
}

//Predicate - consumes a value/values, returns true or false, throws exception
interface Predicate1<T> {
    boolean test(T t) throws Exception;
}

//Function - consumes a value/values, returns result, throws exception
public interface Function1<T, R> {
    R apply(T t) throws Throwable;
}

...
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.