Melaksanakan Runnable vs Extending a Thread

1. Pengenalan

"Perlukah saya melaksanakan Runnable atau memperluas kelas Thread "? adalah soalan biasa.

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

2. Menggunakan Benang

Mari kita tentukan kelas SimpleThread yang meluaskan Thread :

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

Mari lihat juga bagaimana kita dapat menjalankan urutan jenis ini:

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

Kami juga boleh menggunakan ExecutorService untuk melaksanakan utas:

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

Itu cukup banyak kod untuk menjalankan operasi log tunggal dalam utas yang berasingan.

Juga, perhatikan bahawa SimpleThread tidak dapat memperluas kelas lain , kerana Java tidak menyokong banyak warisan.

3. Melaksanakan Runnable

Sekarang, mari buat tugas mudah yang melaksanakan antara muka 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 dalam utas yang berasingan.

Terdapat pelbagai pendekatan yang boleh kita gunakan untuk menjalankannya; salah satunya ialah 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(); }

Kita juga boleh menggunakan Perkhidmatan Pelaksana :

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

Kita boleh membaca lebih lanjut mengenai ExecutorService di sini.

Oleh kerana kita sekarang melaksanakan antara muka, kita bebas untuk memperluas kelas asas lain jika perlu.

Bermula dengan Java 8, setiap antara muka yang memperlihatkan satu metode abstrak dianggap sebagai antara muka fungsional, yang menjadikannya sasaran ekspresi lambda yang sah.

Kita boleh menulis semula kod Runnable di atas menggunakan ungkapan lambda :

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

4. Dijalankan atau Benang ?

Ringkasnya, kami secara amnya menggalakkan penggunaan Runnable over Thread :

  • Semasa memperluas kelas Thread , kami tidak mengesampingkan kaedahnya. Sebaliknya, kita mengatasi kaedah Runnable ( yang mana Thread kebetulan dilaksanakan ) . Ini adalah pelanggaran yang jelas terhadap prinsip IS-A Thread
  • Membuat pelaksanaan Runnable dan meneruskannya ke kelas Thread menggunakan komposisi dan bukan pewarisan - yang lebih fleksibel
  • Setelah melanjutkan kelas Thread , kami tidak dapat melanjutkan kelas lain
  • Dari Java 8 dan seterusnya, Runnables dapat ditunjukkan sebagai ungkapan lambda

5. Kesimpulan

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

Kod untuk siaran ini boleh didapati di GitHub.