Menerapkan Runnable vs Memperluas Thread

1. Perkenalan

“Haruskah saya menerapkan Runnable atau memperpanjang kelas Thread ”? adalah pertanyaan yang cukup umum.

Dalam artikel ini, kita akan melihat pendekatan mana yang lebih masuk akal dalam praktiknya dan mengapa.

2. Menggunakan Thread

Pertama-tama mari kita tentukan kelas SimpleThread yang memperluas Thread :

public class SimpleThread extends Thread { private String message; // standard logger, constructor @Override public void run() { log.info(message); } }

Mari kita lihat juga bagaimana kita dapat menjalankan utas jenis ini:

@Test public void givenAThread_whenRunIt_thenResult() throws Exception { Thread thread = new SimpleThread( "SimpleThread executed using Thread"); thread.start(); thread.join(); }

Kita juga bisa menggunakan ExecutorService untuk mengeksekusi utas:

@Test public void givenAThread_whenSubmitToES_thenResult() throws Exception { executorService.submit(new SimpleThread( "SimpleThread executed using ExecutorService")).get(); }

Itu kode yang cukup banyak untuk menjalankan operasi log tunggal di utas terpisah.

Juga, perhatikan bahwa SimpleThread tidak dapat memperluas kelas lain , karena Java tidak mendukung banyak pewarisan.

3. Menerapkan Runnable

Sekarang, mari buat tugas sederhana yang mengimplementasikan antarmuka java.lang.Runnable :

class SimpleRunnable implements Runnable { private String message; // standard logger, constructor @Override public void run() { log.info(message); } }

SimpleRunnable di atas hanyalah tugas yang ingin kita jalankan di utas terpisah.

Ada berbagai pendekatan yang bisa kita gunakan untuk menjalankannya; salah satunya adalah dengan menggunakan kelas Thread :

@Test public void givenRunnable_whenRunIt_thenResult() throws Exception { Thread thread = new Thread(new SimpleRunnable( "SimpleRunnable executed using Thread")); thread.start(); thread.join(); }

Kami bahkan dapat menggunakan ExecutorService :

@Test public void givenARunnable_whenSubmitToES_thenResult() throws Exception { executorService.submit(new SimpleRunnable( "SimpleRunnable executed using ExecutorService")).get(); }

Kami dapat membaca lebih lanjut tentang ExecutorService di sini.

Karena kami sekarang menerapkan antarmuka, kami bebas untuk memperluas kelas dasar lain jika perlu.

Dimulai dengan Java 8, setiap antarmuka yang mengekspos metode abstrak tunggal diperlakukan sebagai antarmuka fungsional, yang menjadikannya target ekspresi lambda yang valid.

Kita dapat menulis ulang kode Runnable di atas menggunakan ekspresi lambda :

@Test public void givenARunnableLambda_whenSubmitToES_thenResult() throws Exception { executorService.submit( () -> log.info("Lambda runnable executed!")); }

4. Runnable atau Thread ?

Sederhananya, kami biasanya mendorong penggunaan Runnable over Thread :

  • Saat memperluas kelas Thread , kami tidak menimpa salah satu metodenya. Sebagai gantinya, kami mengganti metode Runnable ( yang kebetulan diterapkan oleh Thread ) . Ini jelas melanggar prinsip IS-A Thread
  • Membuat implementasi Runnable dan meneruskannya ke kelas Thread menggunakan komposisi dan bukan pewarisan - yang lebih fleksibel
  • Setelah memperluas kelas Thread , kami tidak dapat memperluas kelas lain
  • Mulai Java 8 dan seterusnya, Runnable dapat direpresentasikan sebagai ekspresi lambda

5. Kesimpulan

Dalam tutorial singkat ini, kami melihat bagaimana mengimplementasikan Runnable biasanya merupakan pendekatan yang lebih baik daripada memperluas kelas Thread .

Kode untuk posting ini dapat ditemukan di GitHub.