2010-10-14 21 views
153

Tôi nên chọn như thế nào giữa ExecutorServicesubmit hoặc execute, nếu giá trị trả về không phải là mối quan tâm của tôi?Chọn giữa việc thực hiện của ExecutorService và ExecutorService thực hiện

Nếu tôi kiểm tra cả hai, tôi không thấy bất kỳ sự khác biệt nào giữa hai ngoại trừ giá trị trả lại.

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

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

Trả lời

166

Có sự khác biệt liên quan đến xử lý lỗi/ngoại lệ.

Nhiệm vụ được xếp hàng với execute() tạo ra một số Throwable sẽ làm cho số UncaughtExceptionHandler cho hoạt động được gọi. Mặc định UncaughtExceptionHandler, thường in dấu vết ngăn xếp Throwable tới System.err, sẽ được gọi nếu không có trình xử lý tùy chỉnh nào được cài đặt.

Mặt khác, một Throwable được tạo ra bởi một nhiệm vụ xếp hàng với submit() sẽ ràng buộc Throwable đến Future đã được sản xuất từ ​​các cuộc gọi đến submit(). Gọi số get() vào số Future sẽ ném một số ExecutionException với nguyên bản chính là số Throwable (có thể truy cập bằng cách gọi getCause() trên số ExecutionException).

+17

Lưu ý rằng hành vi này không được đảm bảo vì nó phụ thuộc vào việc 'Runnable' của bạn có được bao bọc trong một' Task' hay không, mà bạn có thể không kiểm soát được. Ví dụ, nếu 'Executor' của bạn thực sự là một' ScheduledExecutorService', nhiệm vụ của bạn sẽ được bọc trong một 'Future' và uncaught' Throwable 'sẽ bị ràng buộc với đối tượng này. – rxg

+4

Tôi có nghĩa là 'bọc trong' Tương lai 'hay không', tất nhiên. Xem Javadoc cho [ScheduledThreadPoolExecutor # execute] (http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ScheduledThreadPoolExecutor.html#execute (java.lang.Runnable)), ví dụ . – rxg

7

Taken từ Javadoc:

Phương pháp submit mở rộng phương pháp cơ sở {@link Executor # execute} bằng cách tạo và trả lại một {@link Future} có thể được được sử dụng để hủy thực hiện và/hoặc đợi hoàn tất .

Cá nhân tôi thích sử dụng thực thi hơn vì nó cảm thấy khai báo hơn, mặc dù đây thực sự là vấn đề sở thích cá nhân.

Để cung cấp thêm thông tin: trong trường hợp triển khai ExecutorService, việc triển khai lõi được trả về bằng cách gọi tới số Executors.newSingleThreadedExecutor()ThreadPoolExecutor.

Cuộc gọi submit được cung cấp bởi phụ huynh AbstractExecutorService và tất cả cuộc gọi được thực thi nội bộ. thực thi được ghi đè/cung cấp trực tiếp bởi số ThreadPoolExecutor.

11

nếu bạn không quan tâm đến kiểu trả về, hãy sử dụng lệnh thực thi. nó giống như gửi, mà không có sự trở lại của tương lai.

+9

Điều này không đúng theo câu trả lời được chấp nhận. Xử lý ngoại lệ là một sự khác biệt khá đáng kể. – Zero3

2

Từ Javadoc:

Lệnh này có thể thực hiện trong một chủ đề mới, trong một thread gộp, hoặc trong tiểu trình đang gọi, theo ý của việc thực hiện Executor.

Vì vậy, tùy thuộc vào việc triển khai Executor bạn có thể thấy rằng việc gửi chuỗi chặn trong khi tác vụ đang thực thi.

34

thực hiện: Sử dụng nó cho lửa và quên đi cuộc gọi

nộp: Sử dụng nó để kiểm tra kết quả của lời gọi phương thức và có hành động thích hợp trên Future phản đối được trả về bởi các cuộc gọi

Từ javadocs

submit(Callable<T> task)

Gửi nhiệm vụ trả về giá trị để thực thi và trả về Tương lai trình bày kết quả đang chờ xử lý.

Future<?> submit(Runnable task)

nộp một nhiệm vụ Runnable để thực hiện và trả về một tương lai đại diện cho rằng nhiệm vụ.

void execute(Runnable command) 

Thực thi các lệnh được đưa ra tại một số thời gian trong tương lai. Lệnh này có thể thực hiện trong một luồng mới, trong một luồng gộp, hoặc trong chuỗi gọi, theo quyết định của việc thực thi Executor.

Bạn phải thận trọng khi sử dụng submit(). Nó ẩn ngoại lệ trong khung chính nó trừ khi bạn nhúng mã tác vụ của mình vào khối try{} catch{}.

Mã mẫu: Mã này nuốt 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(); 
    } 
} 

đầu ra:

java ExecuteSubmitDemo 
creating service 
a and b=4:0 

cùng một mã ném bằng cách thay thế submit() với execute():

Thay

service.submit(new Runnable(){ 

với

service.execute(new Runnable(){ 

đầu ra:

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) 

Làm thế nào để xử lý các loại kịch bản khi sử dụng trình()?

  1. Nhúng mã của bạn Task (Hoặc Runnable hoặc Callable thực hiện) với try {} catch {} khối mã
  2. Thực hiện CustomThreadPoolExecutor

giải pháp mới:

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

kết quả đầu ra:

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

Chắc chắn là câu trả lời hay nhất ở đây. – smeeb

+0

Giải thích rõ nét. Mặc dù mở rộng nó KHÔNG thực sự cần thiết. Chỉ cần đối tượng trong tương lai phải được tiêu thụ để biết liệu nhiệm vụ có thành công hay không. do đó, sử dụng gửi() nếu bạn đang có kế hoạch để tiêu thụ tương lai nếu không chỉ đơn giản là sử dụng thực hiện() – prash

0

Câu trả lời đầy đủ là một thành phần của hai câu trả lời đã được xuất bản ở đây (cộng với một chút "thêm"):

  • Bằng việc gửi một nhiệm vụ (so với thực hiện nó) bạn lấy lại một tương lai có thể được sử dụng để có được kết quả hoặc hủy bỏ hành động. Bạn không cần phải loại tầm kiểm soát khi bạn execute (vì trở lại loại id của nó void)
  • execute hy vọng một Runnable khi submit có thể mất hoặc là một Runnable hoặc một Callable như một cuộc tranh cãi (để biết thêm về sự khác biệt giữa hai - xem bên dưới).
  • execute bong bóng lên bất kỳ không được kiểm soát-ngoại lệ ngay lập tức (nó không thể ném ngoại lệ kiểm tra !!!), trong khi submit liên kết với bất kỳ loại ngoại lệ đối với tương lai mà trả về kết quả là, và chỉ khi bạn gọi future.get() một sự (bọc) ngoại lệ sẽ được ném. The Throwable mà bạn sẽ nhận được là một ví dụ của ExecutionException và nếu bạn sẽ gọi đối tượng này là getCause(), nó sẽ trả lại Throwable ban đầu.

Một vài chi tiết (có liên quan) điểm:

  • Ngay cả khi nhiệm vụ mà bạn muốn submit không yêu cầu trả lại một kết quả , bạn vẫn có thể sử dụng Callable<Void> (thay vì sử dụng một Runnable).
  • Việc hủy công việc có thể được thực hiện bằng cơ chế interrupt. Dưới đây là an example làm thế nào để thực hiện một chính sách hủy

Tóm lại, đó là một thực hành tốt hơn để sử dụng submit với một Callable (so với execute với một Runnable). Và tôi sẽ báo giá từ "đồng thời Java trong thực tế" Tác giả Brian Goetz:

6.3.2 nhiệm vụ Result chịu: Callable và khung Future

Các Executor sử dụng Runnable như đại diện nhiệm vụ cơ bản của nó. Runnable là khá trừu tượng hạn chế; chạy không thể trả về một giá trị hoặc ném ngoại lệ được kiểm tra, mặc dù nó có thể có tác dụng phụ như ghi vào nhật ký tệp hoặc đặt kết quả trong cấu trúc dữ liệu được chia sẻ.Nhiều nhiệm vụ là tính toán được hoãn lại hiệu quả — thực hiện truy vấn cơ sở dữ liệu, tìm nạp tài nguyên qua mạng hoặc tính toán một hàm phức tạp. Đối với các loại tác vụ này, Có thể gọi là trừu tượng tốt hơn: nó mong đợi rằng điểm vào chính, cuộc gọi, sẽ trả lại giá trị và dự đoán rằng nó có thể ném ngoại lệ.7 Các công trình bao gồm một số phương thức nhiệm vụ, bao gồm Runnable và java.security.PrivilegedAction, với một cuộc gọi.

Các vấn đề liên quan