2008-09-26 25 views
378

Sự khác nhau giữa việc sử dụng các giao diện Runnable và Callable khi thiết kế một chuỗi đồng thời trong Java là gì, tại sao bạn chọn cái kia?Sự khác biệt giữa các giao diện Runnable và Callable trong Java

+2

Để thảo luận thêm, sau khi đọc trang này, hãy xem [Được gọi có thể được ưa thích trên Runnable?] (Http://stackoverflow.com/q/16757142/1005481) –

Trả lời

357

Xem giải thích here.

Giao diện Callable cũng tương tự như Runnable, trong đó cả hai đều được thiết kế cho các lớp học có trường hợp được có khả năng thực hiện bởi một chủ đề. Một Runnable, tuy nhiên, không trả lại kết quả và không thể ném một ngoại lệ đã kiểm tra .

31

Tôi thấy điều này trong blog khác mà có thể giải thích nó một chút hơn những differences:

Mặc dù cả hai giao diện được thực hiện bởi các lớp có nhu cầu thực hiện trong một thread khác nhau thực hiện, nhưng có rất ít sự khác biệt giữa giao diện hai đó là:

  • một ví dụ Callable<V> trả về một kết quả của loại V, trong khi một trường hợp Runnable không.
  • Một ví dụ Callable<V> thể ném ngoại lệ kiểm tra, trong khi một trường hợp Runnable

Các nhà thiết kế của Java không thể cảm thấy một nhu cầu mở rộng khả năng của giao diện Runnable, nhưng họ không muốn làm ảnh hưởng đến công dụng của giao diện Runnable và có lẽ đó là lý do tại sao họ đã có một giao diện riêng biệt có tên Callable trong Java 1.5 thay vì đã thay đổi Runnable hiện có.

223

Sự khác biệt trong các ứng dụng của RunnableCallable là gì. Sự khác biệt chỉ với tham số trả về có trong Callable?

Về cơ bản, có. Xem câu trả lời cho this question. Và javadoc for Callable.

Nhu cầu có cả hai nếu Callable có thể làm tất cả những gì Runnable không?

Bởi vì giao diện Runnablekhông thể làm tất cả những gì Callable không!

Runnable đã có từ Java 1.0, nhưng Callable chỉ được giới thiệu trong Java 1.5 ... để xử lý các trường hợp sử dụng mà Runnable không hỗ trợ. Về lý thuyết, nhóm Java có thể đã thay đổi chữ ký của phương thức Runnable.run(), nhưng điều này sẽ có khả năng tương thích nhị phân bị hỏng với mã pre-1.5, đòi hỏi phải mã hóa khi di chuyển mã Java cũ sang các JVM mới hơn. Đó là một BIG NO-NO. Java cố gắng tương thích ngược ... và đó là một trong những điểm bán hàng lớn nhất của Java cho tính toán kinh doanh.

Và, hiển nhiên, có các trường hợp sử dụng khi tác vụ không cần để trả lại kết quả hoặc ném ngoại lệ đã kiểm tra. Đối với các trường hợp sử dụng đó, việc sử dụng Runnable ngắn gọn hơn sử dụng Callable<Void> và trả về giá trị giả (null) từ phương thức call().

+4

Tôi tự hỏi bạn đã lấy lịch sử này từ đâu. Điều này rất hữu ích. – spiderman

+3

@prash - các sự kiện cơ bản được tìm thấy trong sách giáo khoa cũ. Giống như ấn bản đầu tiên của Java trong một Nutshell. –

+1

(@prash - Ngoài ra ... bằng cách bắt đầu sử dụng Java trong thời kỳ Java 1.1.) –

8

Vì nó đã được đề cập ở đây Có thể gọi là giao diện tương đối mới và được giới thiệu như một phần của gói đồng thời. Cả hai cuộc gọi và Runnable có thể được sử dụng với các nhà điều hành. Class Thread (thực hiện Runnable chính nó) chỉ hỗ trợ Runnable.

Bạn vẫn có thể sử dụng Runnable with executors. Ưu điểm của Callable là bạn có thể gửi nó đến người thực hiện và ngay lập tức lấy lại kết quả tương lai sẽ được cập nhật khi thực hiện xong. Điều tương tự có thể được thực hiện với Runnable, nhưng trong trường hợp này bạn phải tự quản lý kết quả. Ví dụ: bạn có thể tạo hàng đợi kết quả sẽ giữ tất cả kết quả. Chủ đề khác có thể chờ trên hàng đợi này và xử lý các kết quả đến.

+0

tôi tự hỏi ví dụ về một thread ném ngoại lệ trong java là gì? chủ đề chính có thể bắt được ngoại lệ đó không? Nếu không, tôi sẽ không sử dụng Callable. Alex, bạn có hiểu biết về điều này không? cảm ơn! – trillions

+1

Mã chạy trong chuỗi tùy chỉnh vì bất kỳ mã nào khác đều có thể ném ngoại lệ. Để bắt nó trong chủ đề khác, bạn phải thực hiện một số nỗ lực hoặc bằng cách sử dụng cơ chế thông báo tùy chỉnh (ví dụ như dựa trên người nghe) hoặc bằng cách sử dụng 'Tương lai' hoặc bằng cách thêm móc bắt tất cả ngoại lệ không được chú ý: http://docs.oracle.com/javase /6/docs/api/java/lang/Thread.html#setDefaultUncaughtExceptionHandler%28java.lang.Thread.UncaughtExceptionHandler%29 – AlexR

+0

Thông tin tuyệt vời! Cảm ơn, Alex! :) – trillions

63
  • Một Callable cần phải thực hiện call() phương pháp trong khi một Runnable nhu cầu để thực hiện run() phương pháp.
  • A Callable có thể trả lại giá trị nhưng không thể trả lại giá trị Runnable.
  • A Callable có thể ném ngoại lệ đã kiểm tra nhưng không thể Runnable.
  • A Callable có thể được sử dụng với ExecutorService#invokeXXX(Collection<? extends Callable<T>> tasks) phương pháp nhưng không thể là Runnable.

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

ExecutorService.submit (nhiệm vụ Runnable) cũng tồn tại và rất hữu ích –

+0

Runnable cũng có thể được sử dụng với ExecutorService bằng cách thực hiện theo cách 1) ExecutorService.execute (Runnable) 2) ExecutorService.submit (Runnable) –

+0

Ngoài ra còn có Executor.submit (Callable task) nhưng bạn không thể invokeAll hoặc invokeAny với bộ sưu tập của Runnable tasks Collection > – nikli

21

Chúng ta hãy nhìn vào nơi người ta sẽ sử dụng Runnable và Callable.

Runnable và Callable cả hai đều chạy trên một chuỗi khác với chuỗi đang gọi. Nhưng Callable có thể trả về một giá trị và Runnable không thể. Vậy điều này thực sự áp dụng ở đâu.

Runnable: Nếu bạn có hỏa hoạn và quên tác vụ thì hãy sử dụng Runnable. Đặt mã của bạn bên trong một Runnable và khi phương thức run() được gọi, bạn có thể thực hiện nhiệm vụ của mình. Chuỗi cuộc gọi thực sự không quan tâm khi bạn thực hiện tác vụ của mình.

Có thể gọi: Nếu bạn đang cố truy xuất giá trị từ tác vụ, thì hãy sử dụng Có thể gọi. Bây giờ có thể gọi một mình sẽ không thực hiện công việc. Bạn sẽ cần một tương lai mà bạn quấn quanh Callable của bạn và nhận được các giá trị của bạn trên future.get(). Ở đây, chuỗi gọi sẽ bị chặn cho đến khi tương lai quay trở lại với kết quả mà đến lượt nó đang đợi phương thức call() của Callable để thực hiện.

Vì vậy, hãy suy nghĩ về một giao diện cho một lớp đích mà bạn có cả hai phương thức Runnable và Callable được định nghĩa. Lớp gọi sẽ ngẫu nhiên gọi các phương thức giao diện của bạn mà không biết đó là Runnable và đó là Callable. Các phương thức Runnable sẽ thực hiện không đồng bộ, cho đến khi một phương thức Callable được gọi. Ở đây, chuỗi của lớp gọi sẽ chặn vì bạn đang lấy các giá trị từ lớp đích của bạn. Chú ý: Bên trong lớp mục tiêu của bạn, bạn có thể thực hiện các cuộc gọi đến Callable và Runnable trên một trình xử lý luồng đơn, làm cho cơ chế này tương tự như một hàng đợi công văn nối tiếp. Vì vậy, miễn là người gọi gọi các phương thức Runnable của bạn, chuỗi gọi sẽ thực thi nhanh chóng mà không bị chặn.Ngay sau khi nó gọi một Callable bọc trong tương lai phương pháp nó sẽ phải chặn cho đến khi tất cả các hàng đợi khác được thực hiện. Chỉ khi đó phương thức sẽ trả về với các giá trị. Đây là cơ chế đồng bộ hóa.

14

Callable giao diện tuyên bố call() phương pháp và bạn cần phải cung cấp thuốc generic as type của cuộc gọi Object() sẽ trả về -

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 mặt khác là giao diện mà tuyên bố run() phương pháp đó được gọi là khi bạn tạo một Chủ đề với runnable và bắt đầu cuộc gọi() trên đó. Bạn cũng có thể gọi trực tiếp run() nhưng thực hiện phương thức run() là cùng một luồng.

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

Để tóm tắt vài khác biệt đáng chú ý là

  1. Một đối tượng Runnable không trả lại kết quả là trong khi một đối tượng Callable trả về một kết quả.
  2. Một đối tượng Runnable không thể ném một ngoại lệ được chọn là một đối tượng Callable có thể ném một ngoại lệ .
  3. Giao diện Runnable đã có từ Java 1.0 trong khi Callable chỉ được giới thiệu trong Java 1.5.

ít điểm tương đồng bao gồm

  1. Instances của các lớp mà thực hiện các giao diện Runnable hoặc Callable có tiềm năng thực hiện bởi thread khác.
  2. Instance của cả hai giao diện Callable và Runnable có thể được thực hiện bởi ExecutorService thông qua phương pháp nộp().
  3. Cả hai đều là các giao diện chức năng và có thể được sử dụng trong các biểu thức Lambda từ Java8.

phương pháp trong giao diện ExecutorService là

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

Mục đích của các giao diện từ tài liệu oracle:

Runnable giao diện cần được thực hiện bởi bất kỳ lớp có trường hợp được dự định sẽ được thực hiện bởi một Thread. Lớp phải định nghĩa một phương thức không có đối số được gọi là run.

Callable: Nhiệm vụ trả về kết quả và có thể ném ngoại lệ. Các nhà triển khai xác định một phương thức duy nhất mà không có đối số nào được gọi là cuộc gọi. Giao diện Callable tương tự như Runnable, trong đó cả hai được thiết kế cho các lớp có các phiên bản có khả năng được thực hiện bởi một chuỗi khác. Tuy nhiên, Runnable không trả lại kết quả và không thể ném ngoại lệ đã kiểm tra.

khác biệt:

  1. Bạn có thể vượt qua Runnable để tạo ra một Thread. Nhưng bạn không thể tạo Chủ đề mới bằng cách chuyển thông số Callable làm tham số. Bạn chỉ có thể chuyển qua Gọi đến ExecutorService phiên bản.

    Example:

    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. Sử dụng Runnable cho lửa và quên đi cuộc gọi. Sử dụng Callable để xác minh kết quả.

  3. Callable có thể được chuyển đến phương thức invokeAll không giống như Runnable. Phương pháp invokeAnyinvokeAll thực hiện các hình thức phổ biến hữu ích nhất thực hiện với số lượng lớn, thực hiện một tập hợp các nhiệm vụ và sau đó chờ đợi ít nhất một hoặc tất cả, để hoàn thành

  4. chênh lệch không đáng kể: tên phương pháp được thực hiện =>run() cho Runnablecall() cho Callable.

1

Sự khác nhau giữa Callable và Runnable đang theo dõi:

  1. Callable được giới thiệu trong JDK 5.0 nhưng Runnable được giới thiệu trong JDK 1.0
  2. Callable có cuộc gọi() phương pháp nhưng Runnable đã chạy () phương pháp.
  3. Phương thức gọi có thể gọi trả về giá trị nhưng Phương thức chạy có thể gọi không trả về bất kỳ giá trị nào.
  4. phương thức gọi có thể ném ngoại lệ đã kiểm tra nhưng phương thức chạy không thể ném ngoại lệ đã kiểm tra.
  5. Phương thức submit() sử dụng có thể gọi trong hàng đợi nhiệm vụ nhưng phương thức execute() sử dụng Runnable để đặt vào hàng đợi nhiệm vụ.
3
+-------------------------------------+--------------------------------------------------------------------------------------------------+ 
|    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                  | 
+-------------------------------------+--------------------------------------------------------------------------------------------------+ 

Các nhà thiết kế của Java cảm thấy một nhu cầu mở rộng khả năng của giao diện Runnable, nhưng họ không muốn làm ảnh hưởng đến công dụng của giao diện Runnable và có lẽ đó là lý do tại sao họ đã đi để có một giao diện riêng biệt có tên là Callable trong Java 1.5 thay vì đã thay đổi giao diện Runnable đã tồn tại từ Java 1.0. source

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