2011-02-10 24 views
40

Kể từ khi sử dụng ExecutorService thể submit một nhiệm vụ Callable và trả về một Future, tại sao cần phải sử dụng FutureTask quấn Callable nhiệm vụ và sử dụng phương pháp execute? Tôi cảm thấy cả hai đều làm điều tương tự.Sự khác nhau giữa Future và FutureTask trong Java là gì?

Trả lời

21

Trong thực tế, bạn là chính xác. Hai cách tiếp cận là giống hệt nhau. Bạn thường không cần phải quấn chúng lại. Nếu bạn đang có, bạn đang có khả năng sao chép mã trong AbstractExecutorService:

/** 
* Returns a <tt>RunnableFuture</tt> for the given callable task. 
* 
* @param callable the callable task being wrapped 
* @return a <tt>RunnableFuture</tt> which when run will call the 
* underlying callable and which, as a <tt>Future</tt>, will yield 
* the callable's result as its result and provide for 
* cancellation of the underlying task. 
* @since 1.6 
*/ 
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) { 
    return new FutureTask<T>(callable); 
} 

Sự khác biệt duy nhất giữa tương lai và RunnableFuture, là run() phương pháp:

/** 
* A {@link Future} that is {@link Runnable}. Successful execution of 
* the <tt>run</tt> method causes completion of the <tt>Future</tt> 
* and allows access to its results. 
* @see FutureTask 
* @see Executor 
* @since 1.6 
* @author Doug Lea 
* @param <V> The result type returned by this Future's <tt>get</tt> method 
*/ 
public interface RunnableFuture<V> extends Runnable, Future<V> { 
    /** 
    * Sets this Future to the result of its computation 
    * unless it has been cancelled. 
    */ 
    void run(); 
} 

Một lý do chính đáng để cho các Executor xây dựng FutureTask cho bạn là để đảm bảo rằng không có cách nào có thể có nhiều hơn một tham chiếu tồn tại đối với cá thể FutureTask. Nghĩa là, Người thi hành sở hữu trường hợp này.

+0

FutureTask.get() không bao giờ ném ra một CancellationException, trong khi Future.get() thực hiện. Điều này có đúng không? Xem http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/FutureTask.html#get(long, java.util.concurrent.TimeUnit). –

13

Future chỉ là giao diện. Phía sau hiện trường, việc triển khai thực hiện là FutureTask.

Bạn hoàn toàn có thể sử dụng FutureTask theo cách thủ công nhưng bạn sẽ mất các ưu điểm khi sử dụng Executor (tạo chủ đề, giới hạn chuỗi, v.v.). Sử dụng FutureTask khá giống với sử dụng số Thread cũ và sử dụng phương pháp chạy.

+1

FutureTask thực hiện cả Tương lai và Runnable tại sao nó không thể được gửi đến ExecutorService? –

5

Bạn chỉ cần sử dụng FutureTask nếu bạn muốn thay đổi hành vi của nó hoặc truy cập vào Có thể gọi sau này. Đối với 99% sử dụng, chỉ cần sử dụng Gọi và Tương lai.

38

FutureTask Lớp này cung cấp một base implementation of Future, với các phương pháp để bắt đầu và hủy bỏ một tính toán

Future là giao diện

1

Như Mark, và những người khác, đã trả lời một cách chính xác rằng Future là giao diện cho FutureTaskExecutor hiệu quả nhà máy của nó; có nghĩa là mã ứng dụng hiếm khi khởi tạo trực tiếp FutureTask. Để bổ sung cho các cuộc thảo luận tôi đang cung cấp một ví dụ cho thấy một tình huống mà FutureTask được xây dựng và sử dụng trực tiếp, bên ngoài bất kỳ Executor:

FutureTask<Integer> task = new FutureTask<Integer>(()-> { 
     System.out.println("Pretend that something complicated is computed"); 
     Thread.sleep(1000); 
     return 42; 
    }); 

    Thread t1 = new Thread(()->{ 
     try { 
      int r = task.get(); 
      System.out.println("Result is " + r); 
     } catch (InterruptedException | ExecutionException e) {} 
    }); 
    Thread t2 = new Thread(()->{ 
     try { 
      int r = task.get(); 
      System.out.println("Result is " + r); 
     } catch (InterruptedException | ExecutionException e) {} 
    }); 
    Thread t3 = new Thread(()->{ 
     try { 
      int r = task.get(); 
      System.out.println("Result is " + r); 
     } catch (InterruptedException | ExecutionException e) {} 
    }); 

    System.out.println("Several threads are going to wait until computations is ready"); 
    t1.start(); 
    t2.start(); 
    t3.start(); 
    task.run(); // let the main thread to compute the value 

Ở đây, FutureTask được sử dụng như một công cụ đồng bộ hóa, như CountdownLatch hoặc rào cản tương tự nguyên thủy. Nó có thể đã được tái triển khai bằng cách sử dụng CountdownLatch hoặc ổ khóa và điều kiện; FutureTask chỉ làm cho nó được đóng gói độc đáo, tự giải thích, thanh lịch và ít mã hơn.

Cũng lưu ý rằng phương thức FutureTask # run() phải được gọi một cách rõ ràng, trong bất kỳ chủ đề nào; không có Kẻ Thi Hành nào làm việc đó cho bạn. Trong mã của tôi, nó cuối cùng được thực hiện bởi luồng chính, nhưng có thể sửa đổi phương thức get() để gọi run() trên chuỗi đầu tiên gọi get(), do đó chuỗi đầu tiên đạt đến get() và có thể là bất kỳ T1, T2 hoặc T3 nào, tính toán cho tất cả các chủ đề còn lại.Trên ý tưởng này - kết quả yêu cầu luồng đầu tiên sẽ thực hiện việc tính toán cho người khác, trong khi các nỗ lực đồng thời sẽ bị chặn - là Trình ghi nhớ dựa trên, xem ví dụ Memoizer Cache từ trang 108 trong "Java Concurrency in Practice".