Jawaban:
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.
Apa perbedaan dalam aplikasi
Runnable
danCallable
. Apakah perbedaannya hanya dengan parameter pengembalian adaCallable
?
Pada dasarnya ya. Lihat jawaban untuk pertanyaan ini . Dan javadoc untukCallable
.
Apa perlunya memiliki keduanya jika
Callable
dapat melakukan semua yangRunnable
dilakukannya?
Karena Runnable
antarmuka tidak dapat melakukan semua yang Callable
dilakukannya!
Runnable
telah ada sejak Java 1.0, tetapi Callable
hanya diperkenalkan di Java 1.5 ... untuk menangani kasus penggunaan yang Runnable
tidak 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 Runnable
tersebut , menggunakan lebih ringkas daripada menggunakan Callable<Void>
dan mengembalikan nilai dummy ( null
) dari call()
metode.
Runnable
ada (sebagian besar) untuk alasan kompatibilitas ke belakang. Tapi bukankah ada situasi di mana tidak perlu atau terlalu mahal untuk mengimplementasikan (atau membutuhkan) Callable
antarmuka (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?
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.)
Callable
perlu menerapkan call()
metode sementara Runnable
kebutuhan untuk menerapkan run()
metode.Callable
dapat mengembalikan nilai tetapi Runnable
tidak bisa.Callable
dapat membuang pengecekan yang dicek tetapi Runnable
tidak bisa.A Callable
dapat digunakan dengan ExecutorService#invokeXXX(Collection<? extends Callable<T>> tasks)
metode tetapi Runnable
tidak bisa.
public interface Runnable {
void run();
}
public interface Callable<V> {
V call() throws Exception;
}
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:
Callable<V>
instance mengembalikan hasil dari tipe V
, sedangkan Runnable
instance tidak.Callable<V>
instance dapat membuang pengecualian yang diperiksa, sedangkan sebuah Runnable
instance tidak bisaPara perancang Java merasa perlu memperluas kemampuan Runnable
antarmuka, tetapi mereka tidak ingin memengaruhi penggunaan Runnable
antarmuka dan mungkin itulah alasan mengapa mereka menggunakan antarmuka terpisah bernama Callable
Java 1.5 daripada mengubah yang sudah ada. yang ada Runnable
.
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.
Callable
antarmuka 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;
}
Runnable
di 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
Runnable
objek tidak mengembalikan hasil sedangkan Callable
objek mengembalikan hasilnya.Runnable
objek tidak bisa melempar pengecualian diperiksa wheras sebuah Callable
objek dapat membuang pengecualian.Runnable
antarmuka telah ada sejak Java 1.0 sedangkan Callable
hanya diperkenalkan di Jawa 1.5.Beberapa kesamaan termasuk
Metode dalam antarmuka ExecutorService adalah
<T> Future<T> submit(Callable<T> task);
Future<?> submit(Runnable task);
<T> Future<T> submit(Runnable task, T result);
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 Callable
antarmuka mirip dengan Runnable
, dalam bahwa keduanya dirancang untuk kelas yang contoh yang berpotensi dieksekusi oleh thread lain. Runnable
Namun, A tidak mengembalikan hasil dan tidak bisa melempar pengecualian yang diperiksa.
Perbedaan lainnya:
Anda dapat lulus Runnable
untuk membuat Utas . Tetapi Anda tidak dapat membuat Utas baru dengan melewati Callable
sebagai parameter. Anda dapat meneruskan Callable hanya ke ExecutorService
instance.
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();
}
}
Gunakan Runnable
untuk api dan lupakan panggilan. Gunakan Callable
untuk memverifikasi hasilnya.
Callable
dapat diteruskan ke memanggil semua metode tidak seperti Runnable
. Metode invokeAny
dan invokeAll
melakukan bentuk-bentuk eksekusi massal yang paling umum berguna, mengeksekusi kumpulan tugas dan kemudian menunggu setidaknya satu, atau semua, untuk menyelesaikan
Perbedaan sepele: nama metode yang akan diterapkan => run()
untuk Runnable
dan call()
untuk Callable
.
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.
Future
atau dengan menambahkan kait yang menangkap semua pengecualian yang tidak dipikirkan
Thread
untuk menggunakan Callable
antarmuka 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 ...
+-------------------------------------+--------------------------------------------------------------------------------------------------+
| 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 Runnable
antarmuka, tetapi mereka tidak ingin memengaruhi penggunaan Runnable
antarmuka dan mungkin itulah alasan mengapa mereka menggunakan antarmuka terpisah bernama Callable
Java 1.5 daripada mengubah yang sudah ada. Runnable
antarmuka yang ada yang telah menjadi bagian dari Java sejak Java 1.0. sumber
Perbedaan antara Callable dan Runnable adalah sebagai berikut:
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.
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();
}
}
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;
}
...