Runnable vs. Callable di Java

1. Ikhtisar

Sejak masa awal Java, multithreading telah menjadi aspek utama bahasa. Runnable adalah antarmuka inti yang disediakan untuk merepresentasikan tugas multi-threaded dan Callable adalah versi Runnable yang ditingkatkan yang ditambahkan di Java 1.5.

Pada artikel ini, kita akan membahas perbedaan dan aplikasi dari kedua antarmuka tersebut.

2. Mekanisme Eksekusi

Kedua antarmuka dirancang untuk mewakili tugas yang dapat dijalankan oleh banyak utas. Runnable tugas dapat dijalankan menggunakan Thread kelas atau ExecutorService sedangkan callables dapat dijalankan hanya menggunakan kedua.

3. Mengembalikan Nilai

Mari kita lihat lebih dalam cara antarmuka ini menangani nilai kembalian.

3.1. Dengan Runnable

The Runnable interface antarmuka fungsional dan memiliki satu run () metode yang tidak menerima parameter apapun dan tidak kembali nilai-nilai apapun.

Ini cocok untuk situasi di mana kami tidak mencari hasil dari eksekusi utas, misalnya, pencatatan peristiwa yang masuk:

public interface Runnable { public void run(); }

Mari kita pahami ini dengan sebuah contoh:

public class EventLoggingTask implements Runnable{ private Logger logger = LoggerFactory.getLogger(EventLoggingTask.class); @Override public void run() { logger.info("Message"); } }

Dalam contoh ini, utas hanya akan membaca pesan dari antrian dan memasukkannya ke dalam file log. Tidak ada nilai yang dikembalikan dari tugas; tugas dapat diluncurkan menggunakan ExecutorService:

public void executeTask() { executorService = Executors.newSingleThreadExecutor(); Future future = executorService.submit(new EventLoggingTask()); executorService.shutdown(); }

Dalam kasus ini, objek Future tidak akan memiliki nilai apa pun.

3.2. Dengan Callable

The Callable antarmuka adalah antarmuka generik yang mengandung satu panggilan () metode - yang mengembalikan nilai generik V :

public interface Callable { V call() throws Exception; }

Mari kita lihat cara menghitung faktorial sebuah bilangan:

public class FactorialTask implements Callable { int number; // standard constructors public Integer call() throws InvalidParamaterException { int fact = 1; // ... for(int count = number; count > 1; count--) { fact = fact * count; } return fact; } }

Hasil dari metode call () dikembalikan dalam objek Future :

@Test public void whenTaskSubmitted_ThenFutureResultObtained(){ FactorialTask task = new FactorialTask(5); Future future = executorService.submit(task); assertEquals(120, future.get().intValue()); }

4. Penanganan Pengecualian

Mari kita lihat seberapa cocok mereka untuk penanganan pengecualian.

4.1. Dengan Runnable

Karena tanda tangan metode tidak memiliki klausa "lemparan" yang ditentukan,tidak ada cara untuk menyebarkan pengecualian yang dicentang lebih lanjut.

4.2. Dengan Callable

Metode callable's call () berisi klausa "throws Exception" sehingga kita dapat dengan mudah menyebarkan pengecualian yang dicentang lebih lanjut:

public class FactorialTask implements Callable { // ... public Integer call() throws InvalidParamaterException { if(number < 0) { throw new InvalidParamaterException("Number should be positive"); } // ... } }

Dalam kasus menjalankan Callable menggunakan sebuah ExecutorService, pengecualian dikumpulkan di Masa Depan objek, yang dapat diperiksa dengan membuat panggilan ke Future.get () metode. Ini akan memunculkan ExecutionException - yang membungkus pengecualian asli:

@Test(expected = ExecutionException.class) public void whenException_ThenCallableThrowsIt() { FactorialCallableTask task = new FactorialCallableTask(-5); Future future = executorService.submit(task); Integer result = future.get().intValue(); }

Dalam pengujian di atas, ExecutionException sedang dilemparkan saat kami melewati nomor yang tidak valid. Kita bisa memanggil metode getCause () pada objek pengecualian ini untuk mendapatkan pengecualian asli yang dicentang.

Jika kita tidak melakukan panggilan ke metode get () dari kelas Future - maka pengecualian yang dilemparkan oleh metode call () tidak akan dilaporkan kembali, dan tugas akan tetap ditandai sebagai selesai:

@Test public void whenException_ThenCallableDoesntThrowsItIfGetIsNotCalled(){ FactorialCallableTask task = new FactorialCallableTask(-5); Future future = executorService.submit(task); assertEquals(false, future.isDone()); }

Tes di atas akan berhasil lulus meskipun kami telah membuat pengecualian untuk nilai negatif parameter ke FactorialCallableTask.

5. Kesimpulan

Pada artikel ini, kami telah mempelajari perbedaan antara antarmuka Runnable dan Callable .

Seperti biasa, kode lengkap untuk artikel ini tersedia di GitHub.