Perbedaan antara antarmuka Runnable dan Callable di Jawa

Jawaban:

444

Lihat penjelasannya di sini .

Antarmuka Callable mirip dengan Runnable, di mana keduanya dirancang untuk kelas yang instansnya berpotensi dieksekusi oleh utas lainnya. Namun, Runnable tidak mengembalikan hasil dan tidak bisa melempar pengecualian yang dicentang.

Jorge Ferreira
sumber
270

Apa perbedaan dalam aplikasi Runnabledan Callable. Apakah perbedaannya hanya dengan parameter pengembalian ada Callable?

Pada dasarnya ya. Lihat jawaban untuk pertanyaan ini . Dan javadoc untukCallable .

Apa perlunya memiliki keduanya jika Callabledapat melakukan semua yang Runnabledilakukannya?

Karena Runnableantarmuka tidak dapat melakukan semua yang Callabledilakukannya!

Runnabletelah ada sejak Java 1.0, tetapi Callablehanya diperkenalkan di Java 1.5 ... untuk menangani kasus penggunaan yang Runnabletidak mendukung. Secara teori, tim Java bisa saja mengubah tanda tangan Runnable.run()metode ini, tetapi ini akan merusak kompatibilitas biner dengan kode pra-1.5, yang membutuhkan pengodean ulang saat memigrasi kode Java lama ke JVM yang lebih baru. Itu adalah TIDAK-TIDAK-yang-BESAR. Java berusaha keras untuk kompatibel ke belakang ... dan itu menjadi salah satu nilai jual terbesar Java untuk komputasi bisnis.

Dan, jelas, ada kasus penggunaan di mana tugas tidak perlu mengembalikan hasil atau melemparkan pengecualian yang diperiksa. Untuk kasus penggunaan Runnabletersebut , menggunakan lebih ringkas daripada menggunakan Callable<Void>dan mengembalikan nilai dummy ( null) dari call()metode.

Stephen C
sumber
9
Saya ingin tahu dari mana Anda mendapatkan sejarah ini. Ini sangat berguna.
spiderman
4
@prash - fakta dasar dapat ditemukan di buku teks lama. Seperti edisi pertama Java in a Nutshell.
Stephen C
4
(@prash - Juga ... dengan mulai menggunakan Java di era Java 1.1.)
Stephen C
1
@StephenC Jika saya membaca jawaban Anda dengan benar, Anda menyarankan Runnableada (sebagian besar) untuk alasan kompatibilitas ke belakang. Tapi bukankah ada situasi di mana tidak perlu atau terlalu mahal untuk mengimplementasikan (atau membutuhkan) Callableantarmuka (misalnya, dalam ScheduledFuture<?> ScheduledExecutorService.schedule(Runnable command, long delay, TimeUnit unit))? Jadi bukankah ada manfaat untuk mempertahankan kedua antarmuka dalam bahasa bahkan sejarah tidak memaksa hasil saat ini?
maks
1
@ Max - Yah saya mengatakan itu, dan saya masih setuju dengan itu. Namun, itu adalah alasan kedua. Namun demikian, saya menduga itu Runnable akan dimodifikasi jika tidak ada keharusan untuk menjaga kompatibilitas. The "boilerplate" return null;adalah argumen yang lemah. (Setidaknya, itu akan menjadi keputusan saya ... dalam konteks hipotetis di mana Anda dapat mengabaikan kompatibilitas ke belakang.)
Stephen C
82
  • Sebuah Callableperlu menerapkan call()metode sementara Runnablekebutuhan untuk menerapkan run()metode.
  • A Callabledapat mengembalikan nilai tetapi Runnabletidak bisa.
  • A Callabledapat membuang pengecekan yang dicek tetapi Runnabletidak bisa.
  • A Callabledapat digunakan dengan ExecutorService#invokeXXX(Collection<? extends Callable<T>> tasks)metode tetapi Runnabletidak bisa.

    public interface Runnable {
        void run();
    }
    
    public interface Callable<V> {
        V call() throws Exception;
    }
nikli
sumber
17
ExecutorService.submit (tugas Runnable) juga ada dan sangat berguna
Yair Kukielka
Runnable juga dapat digunakan dengan ExecutorService dengan mengikuti cara- 1) ExecutorService.execute (Runnable) 2) ExecutorService.submit (Runnable)
Azam Khan
2
Juga ada Executor.submit (tugas Callable <T>) tetapi Anda tidak dapat memanggilAll atau invokeAny dengan koleksi Koleksi tugas Runnable <? extends Callable <T>> task
nikli
36

Saya menemukan ini di blog lain yang dapat menjelaskan sedikit lebih banyak perbedaan ini :

Meskipun kedua antarmuka diimplementasikan oleh kelas-kelas yang ingin dieksekusi di utas eksekusi yang berbeda, tetapi ada beberapa perbedaan antara dua antarmuka yaitu:

  • Sebuah Callable<V>instance mengembalikan hasil dari tipe V, sedangkan Runnableinstance tidak.
  • Sebuah Callable<V>instance dapat membuang pengecualian yang diperiksa, sedangkan sebuah Runnableinstance tidak bisa

Para perancang Java merasa perlu memperluas kemampuan Runnableantarmuka, tetapi mereka tidak ingin memengaruhi penggunaan Runnableantarmuka dan mungkin itulah alasan mengapa mereka menggunakan antarmuka terpisah bernama CallableJava 1.5 daripada mengubah yang sudah ada. yang ada Runnable.

amoran
sumber
27

Mari kita lihat di mana orang akan menggunakan Runnable dan Callable.

Runnable dan Callable keduanya berjalan pada utas yang berbeda dari utas panggilan. Tetapi Callable dapat mengembalikan nilai dan Runnable tidak bisa. Jadi di mana ini benar-benar berlaku.

Runnable : Jika Anda memiliki tugas kebakaran dan lupa kemudian gunakan Runnable. Masukkan kode Anda di dalam Runnable dan ketika metode run () dipanggil, Anda dapat melakukan tugas Anda. Utas panggilan benar-benar tidak peduli ketika Anda melakukan tugas Anda.

Callable : Jika Anda mencoba mengambil nilai dari suatu tugas, maka gunakan Callable. Sekarang callable sendiri tidak akan melakukan pekerjaan itu. Anda akan membutuhkan Masa Depan yang dapat Anda gunakan untuk Callable Anda dan dapatkan nilai-nilai Anda di future.get (). Di sini utas panggilan akan diblokir sampai Masa Depan kembali dengan hasil yang pada gilirannya menunggu metode panggilan Callable () untuk dieksekusi.

Jadi pikirkan sebuah antarmuka ke kelas target di mana Anda memiliki metode terbungkus Runnable dan Callable. Kelas panggilan akan memanggil metode antarmuka Anda secara acak tanpa mengetahui Runnable dan yang Callable. Metode Runnable akan mengeksekusi secara tidak sinkron, sampai metode Callable dipanggil. Di sini utas kelas panggilan akan diblokir karena Anda mengambil nilai dari kelas target Anda.

CATATAN: Di dalam kelas target Anda, Anda dapat membuat panggilan ke Callable dan Runnable pada eksekutor thread tunggal, membuat mekanisme ini mirip dengan antrian pengiriman serial. Jadi selama pemanggil memanggil metode terbungkus Runnable Anda, thread panggilan akan mengeksekusi sangat cepat tanpa memblokir. Begitu ia memanggil Callable dibungkus dalam metode Masa Depan itu harus memblokir sampai semua item yang antri dieksekusi. Hanya kemudian metode akan kembali dengan nilai. Ini adalah mekanisme sinkronisasi.

Kris Subramanian
sumber
14

Callableantarmuka menyatakan call()metode dan Anda perlu memberikan obat generik sebagai jenis panggilan Obyek () harus kembali -

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

Runnabledi sisi lain adalah antarmuka yang menyatakan run()metode yang dipanggil saat Anda membuat Thread dengan runnable dan memanggil mulai () di atasnya. Anda juga dapat langsung memanggil run () tapi itu hanya menjalankan metode run () adalah utas yang sama.

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

Untuk meringkas beberapa Perbedaan penting adalah

  1. Sebuah Runnableobjek tidak mengembalikan hasil sedangkan Callableobjek mengembalikan hasilnya.
  2. Sebuah Runnableobjek tidak bisa melempar pengecualian diperiksa wheras sebuah Callableobjek dapat membuang pengecualian.
  3. The Runnableantarmuka telah ada sejak Java 1.0 sedangkan Callablehanya diperkenalkan di Jawa 1.5.

Beberapa kesamaan termasuk

  1. Contoh kelas yang mengimplementasikan antarmuka Runnable atau Callable berpotensi dieksekusi oleh utas lainnya.
  2. Contoh antarmuka Callable dan Runnable dapat dieksekusi oleh ExecutorService melalui metode submit ().
  3. Keduanya adalah antarmuka fungsional dan dapat digunakan dalam ekspresi Lambda sejak Java8.

Metode dalam antarmuka ExecutorService adalah

<T> Future<T> submit(Callable<T> task);
Future<?> submit(Runnable task);
<T> Future<T> submit(Runnable task, T result);
Aniket Thakur
sumber
13

Tujuan dari antarmuka ini dari dokumentasi oracle:

Antarmuka runnable harus diimplementasikan oleh setiap kelas yang instansnya dimaksudkan untuk dieksekusi oleh a Thread. Kelas harus mendefinisikan metode tanpa argumen yang dipanggil run.

Callable : Suatu tugas yang mengembalikan hasil dan dapat mengeluarkan pengecualian. Pelaksana mendefinisikan metode tunggal tanpa argumen yang disebut panggilan. The Callableantarmuka mirip dengan Runnable, dalam bahwa keduanya dirancang untuk kelas yang contoh yang berpotensi dieksekusi oleh thread lain. RunnableNamun, A tidak mengembalikan hasil dan tidak bisa melempar pengecualian yang diperiksa.

Perbedaan lainnya:

  1. Anda dapat lulus Runnableuntuk membuat Utas . Tetapi Anda tidak dapat membuat Utas baru dengan melewati Callablesebagai parameter. Anda dapat meneruskan Callable hanya ke ExecutorServiceinstance.

    Contoh:

    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. Gunakan Runnableuntuk api dan lupakan panggilan. Gunakan Callableuntuk memverifikasi hasilnya.

  3. Callabledapat diteruskan ke memanggil semua metode tidak seperti Runnable. Metode invokeAnydan invokeAllmelakukan bentuk-bentuk eksekusi massal yang paling umum berguna, mengeksekusi kumpulan tugas dan kemudian menunggu setidaknya satu, atau semua, untuk menyelesaikan

  4. Perbedaan sepele: nama metode yang akan diterapkan => run()untuk Runnabledan call()untuk Callable.

Ravindra babu
sumber
11

Seperti yang sudah disebutkan di sini, Callable adalah antarmuka yang relatif baru dan diperkenalkan sebagai bagian dari paket konkurensi. Baik Callable dan Runnable dapat digunakan dengan eksekutor. Kelas Utas (yang mengimplementasikan Runnable sendiri) hanya mendukung Runnable.

Anda masih bisa menggunakan Runnable dengan eksekutor. Keuntungan Callable yang dapat Anda kirimkan ke pelaksana dan segera mendapatkan kembali hasil Masa Depan yang akan diperbarui ketika eksekusi selesai. Hal yang sama dapat diterapkan dengan Runnable, tetapi dalam hal ini Anda harus mengelola hasilnya sendiri. Misalnya Anda dapat membuat antrian hasil yang akan menampung semua hasil. Utas lainnya dapat menunggu di antrean ini dan menangani hasil yang tiba.

AlexR
sumber
saya bertanya-tanya apa contoh pada utas melemparkan pengecualian di java? akankah utas utama dapat menangkap pengecualian itu? Jika tidak, saya tidak akan menggunakan Callable. Alex, apakah Anda memiliki wawasan tentang ini? Terima kasih!
triliunan
1
Kode berjalan di utas khusus karena kode lain dapat membuang pengecualian. Untuk menangkapnya di utas lain Anda harus melakukan beberapa upaya baik menggunakan mekanisme notifikasi khusus (misalnya berdasarkan pendengar) atau dengan menggunakan Futureatau dengan menambahkan kait yang menangkap semua pengecualian yang tidak dipikirkan
AlexR
Info bagus! Terima kasih, Alex! :)
triliunan
1
Saya memutakhirkan jawaban ini karena menegaskan (dengan benar jika diambil pada nilai nominal) seseorang harus menggunakan model kumpulan thread dengan objek yang dapat dipanggil. Kelihatannya hal yang disayangkan tentang ini adalah bahwa seseorang tidak dapat memperluas Threaduntuk menggunakan Callableantarmuka yang bermakna sehingga satu utas dapat disesuaikan untuk melakukan hal-hal yang dapat dipanggil dan hal-hal lain yang mungkin diinginkan pengembang. Jika ada orang yang membaca komentar ini berpikir saya salah, saya ingin tahu lebih baik ...
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                                                                     |
+-------------------------------------+--------------------------------------------------------------------------------------------------+

Para perancang Java merasa perlu memperluas kemampuan Runnableantarmuka, tetapi mereka tidak ingin memengaruhi penggunaan Runnableantarmuka dan mungkin itulah alasan mengapa mereka menggunakan antarmuka terpisah bernama CallableJava 1.5 daripada mengubah yang sudah ada. Runnableantarmuka yang ada yang telah menjadi bagian dari Java sejak Java 1.0. sumber

Premraj
sumber
7

Perbedaan antara Callable dan Runnable adalah sebagai berikut:

  1. Callable diperkenalkan di JDK 5.0 tetapi Runnable diperkenalkan di JDK 1.0
  2. Callable memiliki metode panggilan () tetapi metode Runnable telah menjalankan ().
  3. Callable memiliki metode panggilan yang mengembalikan nilai tetapi Runnable memiliki metode yang tidak mengembalikan nilai apa pun.
  4. metode panggilan dapat melempar pengecualian yang diperiksa, tetapi metode yang dijalankan tidak dapat membuang pengecualian yang diperiksa.
  5. Metode callable use submit () untuk memasukkan antrian tugas tetapi metode Runnable use execute () untuk dimasukkan ke dalam antrian tugas.
Raman Gupta
sumber
Sangat penting untuk menekankan bahwa Pengecualian yang diperiksa , bukan RuntimeException
BertKing
5

Callable dan Runnable keduanya mirip satu sama lain dan dapat digunakan dalam mengimplementasikan utas. Dalam hal menerapkan Runnable Anda harus menerapkan metode run () tetapi dalam kasus callable Anda harus perlu menerapkan metode panggilan () , kedua metode ini bekerja dengan cara yang sama tetapi metode panggilan () memiliki lebih banyak fleksibilitas. Ada beberapa perbedaan di antara mereka.

Perbedaan antara Runnable dan callable seperti di bawah ini -

1) Metode run () dari runnable return void , artinya jika Anda ingin utas Anda mengembalikan sesuatu yang dapat Anda gunakan lebih lanjut maka Anda tidak punya pilihan dengan metode Runnable run () . Ada solusi 'Callable' , Jika Anda ingin mengembalikan sesuatu dalam bentuk objek maka Anda harus menggunakan Callable, bukan Runnable . Antarmuka yang dapat dipanggil memiliki metode 'panggilan ()' yang mengembalikan Objek .

Metode tanda tangan - Runnable->

public void run(){}

Callable->

public Object call(){}

2) Dalam hal metode Runnable run () jika ada pengecualian yang diperiksa muncul maka Anda harus perlu ditangani dengan blok coba tangkap , tetapi dalam kasus metode Callable call () Anda dapat melempar pengecualian yang diperiksa seperti di bawah ini

 public Object call() throws Exception {}

3) Runnable berasal dari versi legacy java 1.0 , tetapi callable datang dalam versi Java 1.5 dengan kerangka Executer .

Jika Anda terbiasa dengan Pelaksana maka Anda harus menggunakan Callable bukan Runnable .

Semoga kamu mengerti.

Sudhakar Pandey
sumber
2

Runnable (vs) Callable menjadi poin ketika kita menggunakan kerangka Executer.

ExecutorService adalah subinterface dari Executor, yang menerima tugas Runnable dan Callable.

Multi-Threading sebelumnya dapat dicapai menggunakan Interface Sejak 1.0 , tetapi di sini masalahnya adalah setelah menyelesaikan tugas utas kami tidak dapat mengumpulkan informasi Threads. Untuk mengumpulkan data, kami dapat menggunakan bidang Statis.Runnable

Contoh Pisahkan utas untuk mengumpulkan setiap data siswa.

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

Untuk mengatasi masalah ini, mereka telah memperkenalkan Sejak 1.5 yang mengembalikan hasil dan dapat melempar pengecualian.Callable<V>

  • Metode Single Abstract : Kedua antarmuka Callable dan Runnable memiliki metode abstrak tunggal, yang berarti mereka dapat digunakan dalam ekspresi lambda di java 8.

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

Ada beberapa cara berbeda untuk mendelegasikan tugas untuk dieksekusi ke ExecutorService .

  • execute(Runnable task):void peti utas baru tetapi tidak memblokir utas atau utas penelepon karena metode ini kembali batal.
  • submit(Callable<?>):Future<?>, submit(Runnable):Future<?>peti utas baru dan blok utas utama saat Anda menggunakan future.get () .

Contoh menggunakan Interfaces Runnable, Callable with Executor framework.

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

Ini adalah semacam konvensi penamaan antarmuka yang cocok dengan pemrograman fungsional

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

...
yoAlex5
sumber