ExecutorService의 제출과 ExecutorService의 실행 중 선택


194

반환 값이 관심사가 아닌 경우 ExecutorService의 submit 또는 execute 중에서 어떻게 선택해야 합니까?

둘 다 테스트하면 반환 값을 제외하고 둘 사이에 차이가 없었습니다.

ExecutorService threadExecutor = Executors.newSingleThreadExecutor();
threadExecutor.execute(new Task());

ExecutorService threadExecutor = Executors.newSingleThreadExecutor();
threadExecutor.submit(new Task());

답변:


204

예외 / 오류 처리와 관련하여 차이가 있습니다.

함께 대기중인 태스크 execute()그 일부는 생성 Throwable의 원인이됩니다 UncaughtExceptionHandler(가)에 대한 Thread호출 할 작업을 실행할 수 있습니다. 사용자 정의 핸들러가 설치되지 않은 경우 UncaughtExceptionHandler일반적으로 Throwable스택 추적을에 인쇄하는 기본값 System.err이 호출됩니다.

한편, Throwable작업에 의해 생성과 함께 대기 submit()의지 바인드 Throwable받는 사람 Future에 대한 호출에서 생산하였습니다 submit(). 호출 하면 원본 을 원인으로 (을 호출 하여 액세스 할 수 get()있음) Future을 던집니다 .ExecutionExceptionThrowablegetCause()ExecutionException


19
이 동작은 제어 할 수없는 Runnable랩핑 여부에 따라 달라 지므로 보장 Task되지 않습니다. 예를 들어 your Executor가 실제로 인 경우 ScheduledExecutorService작업이 내부적으로 래핑 Future되고 잡히지 않은 Throwable이 개체에 바인딩됩니다.
rxg

4
Future물론 '포장 여부'를 의미 합니다. 예를 들어, ScheduledThreadPoolExecutor # execute 에 대한 Javadoc을 참조하십시오 .
rxg 2016 년

61

실행 : 화재에 사용하고 전화를 잊어

제출 : 메소드 호출 결과를 검사하고 호출에Future의해 리턴 된오브젝트에대해적절한 조치를 수행하는 데 사용하십시오.

에서 의 javadoc

submit(Callable<T> task)

실행을 위해 가치 반환 작업을 제출하고 보류중인 작업 결과를 나타내는 Future를 반환합니다.

Future<?> submit(Runnable task)

실행 가능한 실행 가능 태스크를 제출하고 해당 태스크를 나타내는 Future를 리턴합니다.

void execute(Runnable command)

나중에 언젠가 주어진 명령을 실행합니다. 명령은 실행기 구현의 재량에 따라 새 스레드, 풀링 된 스레드 또는 호출 스레드에서 실행될 수 있습니다.

를 사용하는 동안 예방 조치를 취해야 submit()합니다. 작업 코드를 try{} catch{}블록에 포함시키지 않으면 프레임 워크 자체에서 예외가 숨겨 집니다.

예제 코드 : 이 코드는 제비 Arithmetic exception : / by zero입니다.

import java.util.concurrent.*;
import java.util.*;

public class ExecuteSubmitDemo{
    public ExecuteSubmitDemo()
    {
        System.out.println("creating service");
        ExecutorService service = Executors.newFixedThreadPool(10);
        //ExtendedExecutor service = new ExtendedExecutor();
        service.submit(new Runnable(){
                 public void run(){
                    int a=4, b = 0;
                    System.out.println("a and b="+a+":"+b);
                    System.out.println("a/b:"+(a/b));
                    System.out.println("Thread Name in Runnable after divide by zero:"+Thread.currentThread().getName());
                 }
            });
        service.shutdown();
    }
    public static void main(String args[]){
        ExecuteSubmitDemo demo = new ExecuteSubmitDemo();
    }
}

산출:

java ExecuteSubmitDemo
creating service
a and b=4:0

() 로 대체 submit()하여 동일한 코드가 발생합니다 execute.

바꾸다

service.submit(new Runnable(){

service.execute(new Runnable(){

산출:

java ExecuteSubmitDemo
creating service
a and b=4:0
Exception in thread "pool-1-thread-1" java.lang.ArithmeticException: / by zero
        at ExecuteSubmitDemo$1.run(ExecuteSubmitDemo.java:14)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
        at java.lang.Thread.run(Thread.java:744)

submit ()을 사용하는 동안 이러한 유형의 시나리오를 처리하는 방법은 무엇입니까?

  1. try {} catch {} 블록 코드를 사용 하여 작업 코드 ( Runnable 또는 Callable 구현 중 하나)를 포함 시킵니다.
  2. 도구 CustomThreadPoolExecutor

새로운 솔루션 :

import java.util.concurrent.*;
import java.util.*;

public class ExecuteSubmitDemo{
    public ExecuteSubmitDemo()
    {
        System.out.println("creating service");
        //ExecutorService service = Executors.newFixedThreadPool(10);
        ExtendedExecutor service = new ExtendedExecutor();
        service.submit(new Runnable(){
                 public void run(){
                    int a=4, b = 0;
                    System.out.println("a and b="+a+":"+b);
                    System.out.println("a/b:"+(a/b));
                    System.out.println("Thread Name in Runnable after divide by zero:"+Thread.currentThread().getName());
                 }
            });
        service.shutdown();
    }
    public static void main(String args[]){
        ExecuteSubmitDemo demo = new ExecuteSubmitDemo();
    }
}

class ExtendedExecutor extends ThreadPoolExecutor {

   public ExtendedExecutor() { 
       super(1,1,60,TimeUnit.SECONDS,new ArrayBlockingQueue<Runnable>(100));
   }
   // ...
   protected void afterExecute(Runnable r, Throwable t) {
     super.afterExecute(r, t);
     if (t == null && r instanceof Future<?>) {
       try {
         Object result = ((Future<?>) r).get();
       } catch (CancellationException ce) {
           t = ce;
       } catch (ExecutionException ee) {
           t = ee.getCause();
       } catch (InterruptedException ie) {
           Thread.currentThread().interrupt(); // ignore/reset
       }
     }
     if (t != null)
       System.out.println(t);
   }
 }

산출:

java ExecuteSubmitDemo
creating service
a and b=4:0
java.lang.ArithmeticException: / by zero

좋은 설명입니다. 확장 할 필요는 없지만 실제로는 필요하지 않습니다. 작업이 성공했는지 아닌지를 알기 위해 미래의 객체 만 사용해야합니다. 따라서 Future <t>를 사용할 계획이라면 submit ()을 사용하십시오. 그렇지 않으면 단순히 execute ()를 사용하십시오
prash

11

반환 유형에 신경 쓰지 않으면 execute를 사용하십시오. 미래의 귀환없이 제출과 동일합니다.


15
허용 된 답변에 따라 올바르지 않습니다. 예외 처리는 상당히 큰 차이입니다.
Zero3

7

Javadoc에서 가져온 것 :

메소드 는 실행을 취소하거나 완료를 기다리는 데 사용할 수있는 {@link Future}를 작성하고 리턴하여 submit기본 메소드 {@link Executor # execute}를 확장합니다 .

개인적으로 나는 실행이 더 선언적이라고 생각하기 때문에 실행의 사용을 선호합니다. 비록 이것이 실제로 개인적인 선호의 문제입니다.

더 많은 정보를 제공하려면 다음의 경우 ExecutorService구현의 핵심 구현에 호출에 의해 반환되는 Executors.newSingleThreadedExecutor()입니다 ThreadPoolExecutor.

submit전화는 부모가 제공하는 AbstractExecutorService모든 호출은 내부적으로 실행합니다. 실행이 ThreadPoolExecutor직접 재정의 / 제공됩니다 .


2

로부터 자바 독 :

명령은 실행기 구현의 재량에 따라 새 스레드, 풀링 된 스레드 또는 호출 스레드에서 실행될 수 있습니다.

따라서 구현에 따라 Executor작업이 실행되는 동안 제출 스레드가 차단 될 수 있습니다.


1

전체 답변은 여기에 게시 된 두 가지 답변으로 구성되어 있습니다 (약간 "추가").

  • 작업을 제출하면 (vs. 실행) 결과를 얻거나 작업을 취소하는 데 사용할 수있는 미래를 다시 얻을 수 있습니다. 당신은 이러한 종류의 제어가없는 때 execute(그 반환 유형 ID 때문에 void)
  • execute예상합니다 Runnable동안이 submit중 하나 걸릴 수 있습니다 Runnable또는 Callable(- 아래 참조 둘 사이의 차이에 대한 자세한 정보를 원하시면) 인수로가.
  • execute검사되지 않은 예외는 즉시 확인하고 (확인 된 예외를 던질 수 없습니다 !!!) 결과로 반환되는 미래 submit어떤 종류의 예외도 바인딩 future.get()하고 (랩 된) 예외 를 호출 할 때만 throw됩니다. 얻을 수있는 Throwable은 인스턴스 이며이 ExecutionException객체를 호출 getCause()하면 원래 Throwable을 반환합니다.

몇 가지 더 (관련된) 포인트 :

  • 원하는 작업 submit에서 결과를 반환하지 않아도을 사용할 수 있습니다 Callable<Void>(대신 사용 Runnable).
  • 인터럽트 메커니즘을 사용하여 작업 취소를 수행 할 수 있습니다 . 취소 정책을 구현하는 방법 의 예 는 다음과 같습니다.

요약 submit하면, Callable(와 vs)를 execute함께 사용하는 것이 좋습니다 Runnable. Brian Goetz의 "실제로 Java 동시성"을 인용하겠습니다.

6.3.2 결과를내는 작업 : 소명과 미래

Executor 프레임 워크는 Runnable을 기본 작업 표현으로 사용합니다. Runnable은 상당히 제한적인 추상화입니다. run은 로그 파일에 쓰거나 결과를 공유 데이터 구조에 배치하는 등의 부작용이있을 수 있지만 값을 반환하거나 확인 된 예외를 throw 할 수 없습니다. 데이터베이스 쿼리 실행, 네트워크를 통한 리소스 페치 또는 복잡한 기능 계산 등 많은 작업이 계산을 효과적으로 지연시킵니다. 이러한 유형의 작업의 경우 Callable은 더 나은 추상화입니다. 주요 항목 인 call은 값을 반환하고 예외를 throw 할 것으로 예상합니다. Callable이있는 java.security.PrivilegedAction.


1

허용 된 답변에 추가하기 만하면됩니다.

그러나 태스크에서 발생한 예외는 execute ()로 제출 된 태스크에 대해서만 예외를 처리되지 않은 예외 핸들러로 만듭니다. submit ()을 사용하여 실행기 서비스에 제출 된 태스크의 경우, 예외는 태스크 리턴 상태의 일부로 간주됩니다.

출처

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