"Mengimplementasikan Runnable" vs "extends Thread" di Java

2119

Dari waktu yang saya habiskan dengan utas di Jawa, saya telah menemukan dua cara ini untuk menulis utas:

Dengan implements Runnable:

public class MyRunnable implements Runnable {
    public void run() {
        //Code
    }
}
//Started with a "new Thread(new MyRunnable()).start()" call

Atau, dengan extends Thread:

public class MyThread extends Thread {
    public MyThread() {
        super("MyThread");
    }
    public void run() {
        //Code
    }
}
//Started with a "new MyThread().start()" call

Apakah ada perbedaan signifikan dalam dua blok kode ini?

pengguna65374
sumber
57
Terima kasih atas pertanyaan ini, jawabannya menghilangkan banyak kesalahpahaman yang saya miliki. Saya melihat ke cara yang benar untuk melakukan thread Java sebelum SO ada dan ada banyak informasi yang salah / ketinggalan jaman di luar sana.
James McMahon
5
ada satu alasan Anda mungkin ingin memperpanjang Thread ( tapi saya tidak merekomendasikannya ), Anda dapat menangani terlebih dahulu interrupt(). Sekali lagi, ini adalah ide, mungkin berguna dalam kasus yang tepat, namun saya tidak merekomendasikannya.
bestsss
Silakan lihat juga jawabannya, dijelaskan dengan baik: stackoverflow.com/q/5562720/285594
@bestsss, saya mencoba mencari tahu apa yang Anda maksud tentang penanganan interrupt (). Apakah Anda mencoba mengganti metode tersebut?
Bob Cross
8
ya. Sesuai kode, kelas Thread A dapat memperluas kelas mana pun sedangkan kelas Thread B tidak bisa memperpanjang kelas lain
mani deepak

Jawaban:

1673

Ya: implementasi Runnableadalah cara yang disukai untuk melakukannya, IMO. Anda tidak benar-benar mengkhususkan pada perilaku utas. Anda hanya memberikannya sesuatu untuk dijalankan. Itu berarti komposisi adalah cara filosofis "lebih murni" untuk melangkah.

Secara praktis , ini berarti Anda dapat mengimplementasikan Runnabledan memperluas dari kelas lain juga.

Jon Skeet
sumber
151
Tepat, dengan baik. Perilaku apa yang kami coba timpa di Thread dengan memperluasnya? Saya berpendapat sebagian besar orang tidak mencoba menimpa perilaku apa pun, tetapi mencoba menggunakan perilaku Thread.
hooknc
87
Sebagai komentar sampingan, jika Anda instantiate Thread dan jangan panggil metode start () Anda membuat kebocoran memori di Java <5 (ini tidak terjadi dengan Runnables): stackoverflow.com/questions/107823/…
Nacho Coloma
25
Satu keuntungan kecil dari Runnable adalah, jika dalam keadaan tertentu Anda tidak peduli, atau tidak ingin menggunakan threading, dan Anda hanya ingin mengeksekusi kode, Anda memiliki opsi untuk memanggil run (). misalnya (sangat handwavy) if (numberCores > 4) myExecutor.excute(myRunnable); else myRunnable.run()
user949300
10
@ user949300 Anda juga dapat melakukannya dengan extends Threaddan jika Anda tidak ingin threading mengapa Anda bahkan mengimplementasikan Runnable...
m0skit0
30
Mengutip Sierra dan Bates, manfaat utama penerapan Runnable adalah Anda secara arsitektur memisahkan "pekerjaan" dari "pelari".
8bitjunkie
569

tl; dr: mengimplementasikan Runnable lebih baik. Namun, peringatan itu penting

Secara umum, saya akan merekomendasikan menggunakan sesuatu seperti Runnabledaripada Threadkarena itu memungkinkan Anda untuk menjaga pekerjaan Anda hanya longgar ditambah dengan pilihan konkurensi Anda. Misalnya, jika Anda menggunakan a Runnabledan memutuskan nanti bahwa ini sebenarnya tidak memerlukannya sendiri Thread, Anda dapat memanggil threadA.run ().

Peringatan: Di sekitar sini, saya sangat tidak menyarankan penggunaan Thread mentah. Saya lebih suka menggunakan Callables dan FutureTasks (Dari javadoc: "Sebuah perhitungan asinkron yang dapat dibatalkan"). Integrasi timeout, pembatalan yang tepat, dan penggabungan thread dari dukungan konkurensi modern jauh lebih bermanfaat bagi saya daripada tumpukan Thread mentah.

Tindak lanjut: ada FutureTaskkonstruktor yang memungkinkan Anda untuk menggunakan Runnables (jika itu yang Anda paling nyaman) dan masih mendapatkan manfaat dari alat konkurensi modern. Mengutip javadoc :

Jika Anda tidak membutuhkan hasil tertentu, pertimbangkan untuk menggunakan konstruksi formulir:

Future<?> f = new FutureTask<Object>(runnable, null)

Jadi, jika kami ganti runnabledengan Anda threadA, kami mendapatkan yang berikut:

new FutureTask<Object>(threadA, null)

Opsi lain yang memungkinkan Anda untuk lebih dekat dengan Runnables adalah ThreadPoolExecutor . Anda dapat menggunakan metode eksekusi untuk meneruskan di Runnable untuk mengeksekusi "tugas yang diberikan suatu saat nanti."

Jika Anda ingin mencoba menggunakan kumpulan utas, fragmen kode di atas akan menjadi sesuatu seperti yang berikut (menggunakan metode pabrik Executors.newCachedThreadPool () ):

ExecutorService es = Executors.newCachedThreadPool();
es.execute(new ThreadA());
Bob Cross
sumber
40
Ini lebih baik daripada jawaban IMHO yang diterima. Satu hal: potongan kode yang Anda miliki tidak menutup eksekutor dan saya melihat jutaan pertanyaan di mana orang-orang salah, menciptakan Pelaksana baru setiap kali mereka ingin menelurkan tugas. esakan lebih baik sebagai bidang statis (atau disuntikkan) sehingga hanya akan dibuat sekali.
artbristol
7
@artbristol, terima kasih! Saya tidak setuju dengan Pelaksana baru (kami melakukan apa yang Anda sarankan dalam kode kami). Dalam menulis jawaban asli, saya mencoba untuk menulis kode minimal analog dengan fragmen asli. Kita harus berharap bahwa banyak pembaca jawaban ini menggunakannya sebagai titik melompat. Saya tidak mencoba menulis pengganti javadoc. Saya secara efektif menulis materi pemasaran untuk itu: jika Anda menyukai metode ini, Anda harus melihat semua hal hebat lain yang kami tawarkan ...!
Bob Cross
3
Saya tahu saya agak terlambat mengomentari hal ini, tetapi berurusan FutureTasksecara langsung umumnya bukan yang ingin Anda lakukan. ExecutorServices akan membuat sesuai yang Futureuntuk Anda ketika Anda submitseorang Runnable/ Callablekepada mereka. Demikian juga untuk ScheduledExecutorServices dan ScheduledFutureketika Anda schedulesebuah Runnable/ Callable.
Powerlord
3
@ Powerlord, maksud saya adalah membuat fragmen kode yang cocok dengan OP sedekat mungkin. Saya setuju bahwa FutureTask baru tidak optimal tetapi jelas untuk tujuan penjelasan.
Bob Cross
257

Pesan moral dalam cerita:

Mewarisi hanya jika Anda ingin menimpa beberapa perilaku.

Atau lebih tepatnya harus dibaca sebagai:

Warisan lebih sedikit, antarmuka lebih banyak.

panzerschreck
sumber
1
Ini harus selalu menjadi pertanyaan jika Anda mulai membuat Obyek berjalan bersamaan! Apakah Anda bahkan memerlukan fungsi Obyek Thread?
Liebertee
4
Saat mewarisi dari Thread, seseorang hampir selalu ingin mengesampingkan perilaku run()metode.
Warren Dew
1
Anda tidak dapat mengesampingkan perilaku a java.lang.Threaddengan mengganti run()metode. Dalam hal ini Anda perlu menimpa start()metode yang saya kira. Biasanya Anda hanya menggunakan kembali perilaku java.lang.Threaddengan menyuntikkan blok eksekusi Anda ke run()metode.
sura2k
Warisan tidak hanya untuk mengesampingkan beberapa perilaku, juga untuk menggunakan perilaku umum. Dan sebaliknya, semakin ditimpa, hirarki worser.
kacang
232

Yah begitu banyak Jawaban yang baik, saya ingin menambahkan lebih banyak tentang ini. Ini akan membantu untuk memahami Extending v/s Implementing Thread.
Memperpanjang mengikat dua file kelas sangat erat dan dapat menyebabkan beberapa sangat sulit untuk berurusan dengan kode.

Kedua pendekatan melakukan pekerjaan yang sama tetapi ada beberapa perbedaan.
Perbedaan paling umum adalah

  1. Ketika Anda memperluas kelas Thread, setelah itu Anda tidak dapat memperluas kelas lain yang Anda butuhkan. (Seperti yang Anda tahu, Java tidak memungkinkan mewarisi lebih dari satu kelas).
  2. Ketika Anda mengimplementasikan Runnable, Anda dapat menghemat ruang untuk kelas Anda untuk memperluas kelas lain di masa depan atau sekarang.

Namun, satu perbedaan signifikan antara menerapkan Runnable dan memperluas Thread adalah itu
by extending Thread, each of your threads has a unique object associated with it, whereas implementing Runnable, many threads can share the same object instance.

Contoh berikut akan membantu Anda memahami dengan lebih jelas

//Implement Runnable Interface...
 class ImplementsRunnable implements Runnable {

private int counter = 0;

public void run() {
    counter++;
    System.out.println("ImplementsRunnable : Counter : " + counter);
 }
}

//Extend Thread class...
class ExtendsThread extends Thread {

private int counter = 0;

public void run() {
    counter++;
    System.out.println("ExtendsThread : Counter : " + counter);
 }
}

//Use the above classes here in main to understand the differences more clearly...
public class ThreadVsRunnable {

public static void main(String args[]) throws Exception {
    // Multiple threads share the same object.
    ImplementsRunnable rc = new ImplementsRunnable();
    Thread t1 = new Thread(rc);
    t1.start();
    Thread.sleep(1000); // Waiting for 1 second before starting next thread
    Thread t2 = new Thread(rc);
    t2.start();
    Thread.sleep(1000); // Waiting for 1 second before starting next thread
    Thread t3 = new Thread(rc);
    t3.start();

    // Creating new instance for every thread access.
    ExtendsThread tc1 = new ExtendsThread();
    tc1.start();
    Thread.sleep(1000); // Waiting for 1 second before starting next thread
    ExtendsThread tc2 = new ExtendsThread();
    tc2.start();
    Thread.sleep(1000); // Waiting for 1 second before starting next thread
    ExtendsThread tc3 = new ExtendsThread();
    tc3.start();
 }
}

Output dari program di atas.

ImplementsRunnable : Counter : 1
ImplementsRunnable : Counter : 2
ImplementsRunnable : Counter : 3
ExtendsThread : Counter : 1
ExtendsThread : Counter : 1
ExtendsThread : Counter : 1

Dalam pendekatan antarmuka Runnable, hanya satu instance kelas sedang dibuat dan telah dibagikan oleh utas yang berbeda. Jadi nilai penghitung bertambah untuk setiap akses utas.

Sedangkan, pendekatan kelas Thread, Anda harus membuat turunan terpisah untuk setiap akses utas. Oleh karena itu memori yang berbeda dialokasikan untuk setiap instance kelas dan masing-masing memiliki counter yang terpisah, nilainya tetap sama, yang berarti tidak ada kenaikan akan terjadi karena tidak ada referensi objek yang sama.

Kapan menggunakan Runnable?
Gunakan antarmuka Runnable ketika Anda ingin mengakses sumber daya yang sama dari grup utas. Hindari menggunakan kelas Thread di sini, karena beberapa objek penciptaan mengkonsumsi lebih banyak memori dan itu menjadi overhead kinerja yang besar.

Kelas yang mengimplementasikan Runnable bukan thread dan hanya kelas. Agar Runnable menjadi Thread, Anda harus membuat instance Thread dan mengirimkannya sebagai target.

Dalam kebanyakan kasus, antarmuka Runnable harus digunakan jika Anda hanya berencana untuk menimpa run() metode dan tidak ada metode Thread lainnya. Ini penting karena kelas tidak boleh disubklasifikasikan kecuali programmer berniat memodifikasi atau meningkatkan perilaku mendasar kelas.

Ketika ada kebutuhan untuk memperpanjang superclass, mengimplementasikan antarmuka Runnable lebih tepat daripada menggunakan kelas Thread. Karena kita dapat memperluas kelas lain sambil mengimplementasikan antarmuka Runnable untuk membuat utas.

Saya harap ini akan membantu!

Rupesh Yadav
sumber
40
Kode Anda ternyata salah. Maksud saya, ia melakukan apa yang dilakukannya, tetapi bukan apa yang ingin Anda perlihatkan.
zEro
38
Untuk memperjelas: untuk kasing runnable Anda telah menggunakan instance ImplementsRunnable yang sama untuk memulai beberapa utas, sedangkan untuk kasing Anda membuat instance ExtendsThread yang berbeda yang jelas mengarah pada perilaku yang Anda tunjukkan. Bagian kedua dari metode utama Anda adalah: ExtendsThread et = new ExtendsThread(); Thread tc1 = new Thread(et); tc1.start(); Thread.sleep(1000); Thread tc2 = new Thread(et); tc2.start(); Thread.sleep(1000); Thread tc3 = new Thread(et); tc3.start(); Apakah lebih jelas?
zEro
19
Saya belum mengerti maksud Anda, tetapi poin saya adalah jika Anda membuat beberapa contoh ExtendsThread - semuanya akan mengembalikan 1 (seperti yang telah Anda tunjukkan). Anda bisa mendapatkan hasil yang sama untuk Runnable dengan melakukan hal yang sama di sana, yaitu membuat beberapa instance ImplementsRunnable.
zEro
3
@ zEro Hai, saya dari masa depan. Karena versi kode Anda telah Threadbertambah juga, apakah pernyataan by extending Thread, each of your threads has a unique object associated with it, whereas implementing Runnable, many threads can share the same object instanceitu salah? Jika tidak, lalu kasus apa yang menunjukkan hal ini?
Mesin Cuci Jahat
4
Kode yang diposting di sini menyesatkan dan dapat menyebabkan sakit kepala. Terima kasih kepada @zEro karena telah membersihkannya sedikit.
Nikos
78

Satu hal yang saya terkejut belum disebutkan adalah implementasi Runnablemembuat kelas Anda lebih fleksibel.

Jika Anda memperpanjang utas maka tindakan yang Anda lakukan selalu akan berada di utas. Namun, jika Anda menerapkannya Runnabletidak harus. Anda dapat menjalankannya di utas, atau meneruskannya ke beberapa jenis layanan pelaksana, atau hanya membagikannya sebagai tugas di dalam aplikasi ulir tunggal (mungkin dijalankan di lain waktu, tetapi di utas yang sama). Opsi jauh lebih terbuka jika Anda hanya menggunakan Runnabledaripada jika Anda mengikat diri Thread.

Herms
sumber
6
Yah, Anda sebenarnya bisa melakukan hal yang sama dengan Threadobjek juga karena Thread implements Runnable... ;-) Tetapi "terasa lebih baik" melakukan hal-hal ini dengan Runnabledaripada melakukannya dengan Thread!
siegi
7
Benar, tetapi Threadmenambahkan banyak hal tambahan yang tidak Anda butuhkan, dan dalam banyak kasus tidak mau. Anda selalu lebih baik mengimplementasikan antarmuka yang sesuai dengan apa yang sebenarnya Anda lakukan.
Herms
74

Jika Anda ingin mengimplementasikan atau memperluas kelas lain maka Runnableantarmuka lebih disukai, jika tidak, jika Anda tidak ingin kelas lain diperluas atau diimplementasikan maka Threadkelas lebih disukai.

Perbedaan paling umum adalah

masukkan deskripsi gambar di sini

Ketika Anda extends Threadkelas, setelah itu Anda tidak dapat memperpanjang kelas lain yang Anda butuhkan. (Seperti yang Anda tahu, Java tidak memungkinkan mewarisi lebih dari satu kelas).

Saat Anda implements Runnable, Anda dapat menghemat ruang untuk kelas Anda untuk memperluas kelas lain di masa depan atau sekarang.

  • Java tidak mendukung banyak pewarisan, yang berarti Anda hanya dapat memperluas satu kelas di Jawa sehingga setelah Anda memperluas kelas Utas Anda kehilangan kesempatan dan tidak dapat memperluas atau mewarisi kelas lain di Jawa.

  • Dalam pemrograman berorientasi objek, memperluas kelas biasanya berarti, menambahkan fungsionalitas baru, dan memodifikasi atau meningkatkan perilaku. Jika kami tidak melakukan modifikasi apa pun di Thread, gunakan antarmuka Runnable.

  • Antarmuka yang dapat dijalankan mewakili Tugas yang dapat dieksekusi dengan Thread atau Pelaksana atau cara lain apa pun. jadi pemisahan yang logis dari Tugas sebagai Runnable dari Thread adalah keputusan desain yang baik.

  • Memisahkan tugas sebagai Runnable berarti kita dapat menggunakan kembali tugas dan juga memiliki kebebasan untuk menjalankannya dari cara yang berbeda. karena Anda tidak dapat memulai kembali Thread setelah selesai. lagi Runnable vs Thread untuk tugas, Runnable adalah pemenang.

  • Perancang Java mengakui hal ini dan itulah mengapa Pelaksana menerima Runnable sebagai Tugas dan mereka memiliki utas pekerja yang menjalankan tugas tersebut.

  • Mewarisi semua metode Thread adalah overhead tambahan hanya untuk mewakili Tugas yang dapat dilakukan dengan mudah dengan Runnable.

Atas perkenan dari javarevisited.blogspot.com

Ini adalah beberapa perbedaan penting antara Thread dan Runnable di Jawa. Jika Anda tahu ada perbedaan lain di Thread vs Runnable daripada silakan bagikan melalui komentar. Saya pribadi menggunakan Runnable over Thread untuk skenario ini dan merekomendasikan untuk menggunakan Runnable atau antarmuka Callable berdasarkan kebutuhan Anda.

Namun, perbedaannya adalah.

Saat Anda extends Threadkelas, masing-masing utas Anda membuat objek unik dan mengaitkannya. Saat Anda implements Runnable, itu berbagi objek yang sama ke beberapa utas.

Nidhish Krishnan
sumber
73

Sebenarnya, tidak bijaksana untuk membandingkan Runnabledan Threaddengan satu sama lain.

Keduanya memiliki ketergantungan dan hubungan multi-threading seperti halnya Wheel and Enginehubungan kendaraan bermotor.

Saya akan mengatakan, hanya ada satu cara untuk multi-threading dengan dua langkah. Biarkan saya membuat poin saya.

Runnable:
Ketika mengimplementasikannya interface Runnableberarti Anda membuat sesuatu yang berada run abledi utas yang berbeda. Sekarang membuat sesuatu yang bisa dijalankan di dalam utas (runnable di dalam utas), tidak berarti membuat Utas.
Jadi kelas MyRunnabletidak lain adalah kelas biasa dengan void runmetode. Dan objek itu akan menjadi beberapa objek biasa dengan hanya metode runyang akan dieksekusi secara normal ketika dipanggil. (kecuali jika kita melewatkan objek di utas).

Thread:,
class Thread Saya akan mengatakan kelas yang sangat istimewa dengan kemampuan memulai Thread baru yang sebenarnya memungkinkan multi-threading melalui start()metodenya.

Mengapa tidak bijaksana untuk membandingkan?
Karena kita membutuhkan keduanya untuk multi-threading.

Untuk Multi-threading kita membutuhkan dua hal:

  • Sesuatu yang bisa dijalankan di dalam sebuah Thread (Runnable).
  • Sesuatu yang dapat memulai Thread baru (Thread).

Jadi secara teknis dan teoritis keduanya diperlukan untuk memulai utas, satu akan berjalan dan satu akan membuatnya berjalan (Seperti Wheel and Enginekendaraan bermotor).

Itu sebabnya Anda tidak dapat memulai utas dengan MyRunnableAnda harus meneruskannya ke instance Thread.

Tetapi dimungkinkan untuk membuat dan menjalankan utas hanya menggunakan class Threadkarena Kelas Threadmengimplementasikan Runnablesehingga kita semua tahu Threadjuga ada Runnabledi dalam.

Akhirnya Threaddan Runnablesaling melengkapi satu sama lain untuk multithreading bukan pesaing atau penggantian.

Saif
sumber
3
Persis! Ini harus menjadi jawaban yang diterima. BTW Saya pikir pertanyaannya telah diedit dan ThreadAtidak lagi masuk akal
idelvall
jawaban yang diterima jauh lebih banyak didelegasikan terima kasih atas tanggapan Anda @idelvall
Saif
Jawaban terbaik! Terima kasih!
MichaelYe
44

Anda harus mengimplementasikan Runnable, tetapi jika Anda menjalankan di Java 5 atau lebih tinggi, Anda tidak harus memulainya dengan new Threadtetapi menggunakan ExecutorService sebagai gantinya. Untuk detailnya lihat: Cara mengimplementasikan threading sederhana di Jawa .

Fabian Steeg
sumber
5
Saya tidak akan berpikir ExecutorService akan berguna jika Anda hanya ingin meluncurkan utas tunggal.
Powerlord
1
Dari apa yang telah saya pelajari, seseorang seharusnya tidak lagi memulai utas sendiri secara umum, karena menyerahkannya ke layanan pelaksana membuat semuanya jauh lebih terkontrol (seperti, menunggu utas ditunda). Juga, saya tidak melihat apa pun dalam pertanyaan yang menyiratkan ini tentang satu utas.
Fabian Steeg
4
Apa gunanya menggunakan multi-threading jika kita tahu sebelumnya bahwa itu akan menjadi satu utas. Jadi mari kita asumsikan kita memiliki banyak utas dan jawaban ini sangat berharga.
zEro
1
@ zEro Saya cukup yakin ada alasan hanya ada satu Thread Pengiriman Acara. Saya ragu itu satu-satunya kasus yang terbaik untuk memiliki utas terpisah tetapi mungkin tidak terbaik untuk memiliki beberapa.
porcoesphino
33

Saya bukan ahli, tapi saya bisa memikirkan satu alasan untuk mengimplementasikan Runnable daripada memperpanjang Thread: Java hanya mendukung satu warisan, jadi Anda hanya dapat memperluas satu kelas.

Sunting: Ini awalnya berkata "Menerapkan antarmuka membutuhkan lebih sedikit sumber daya." juga, tetapi Anda harus membuat instance Thread baru, jadi ini salah.

Powerlord
sumber
Dalam runnable kita tidak dapat melakukan panggilan jaringan, bukan? Karena saya memiliki android.os.NetworkOnMainThreadException. Tetapi dengan menggunakan utas saya dapat melakukan panggilan jaringan. Tolong koreksi saya jika saya salah.
Nabeel Thobani
@NabeelThobani Normal Java tidak peduli, tetapi kedengarannya seperti Android. Saya tidak cukup akrab dengan Android untuk dikatakan.
Powerlord
1
@NabeelThobani Tentu saja Anda bisa. Mungkin Anda tidak membuat Thread dengan Runnable Anda.
m0skit0
20

Saya akan mengatakan ada cara ketiga:

public class Something {

    public void justAnotherMethod() { ... }

}

new Thread(new Runnable() {
   public void run() {
    instanceOfSomething.justAnotherMethod();
   }
}).start();

Mungkin ini sedikit dipengaruhi oleh penggunaan Javascript dan Actionscript 3 baru-baru ini, tetapi dengan cara ini kelas Anda tidak perlu mengimplementasikan antarmuka yang agak kabur seperti Runnable.

Bart van Heukelom
sumber
38
Ini sebenarnya bukan cara ketiga. Anda masih menerapkan Runnable, hanya melakukannya secara anonim.
Don Roby
2
@ Don Roby: Yang berbeda. Ini sering kali nyaman, dan Anda bisa menggunakan bidang dan variabel lokal akhir dari kelas / metode yang mengandung.
Bart van Heukelom
6
@ BartvanHeukelom Nyaman, tetapi tidak berbeda. Anda dapat melakukan ini dengan semua jenis kelas bertingkat, yaitu kelas dalam, kelas lokal, dan ekspresi lambda.
xehpuk
18

Dengan rilis Java 8, sekarang ada opsi ketiga.

Runnableadalah antarmuka fungsional , yang berarti bahwa instance dapat dibuat dengan ekspresi lambda atau referensi metode.

Contoh Anda dapat diganti dengan:

new Thread(() -> { /* Code here */ }).start()

atau jika Anda ingin menggunakan ExecutorServicereferensi metode dan:

executor.execute(runner::run)

Ini tidak hanya jauh lebih pendek dari contoh Anda, tetapi juga datang dengan banyak keuntungan yang dinyatakan dalam jawaban lain dari menggunakan Runnablelebih Thread, seperti tanggung jawab tunggal dan menggunakan komposisi karena Anda tidak mengkhususkan perilaku ulir. Cara ini juga menghindari membuat kelas tambahan jika semua yang Anda butuhkan adalah Runnableseperti yang Anda lakukan dalam contoh Anda.

Alex
sumber
Jawaban ini perlu penjelasan. Setelah beberapa membingungkan, saya menyimpulkan bahwa () -> {}seharusnya mewakili logika kebiasaan yang dibutuhkan seseorang? Jadi akan lebih baik dikatakan () -> { /* Code here */ }?
ToolmakerSteve
17

Instantiating antarmuka memberikan pemisahan yang lebih bersih antara kode Anda dan penerapan utas, jadi saya lebih suka menerapkan Runnable dalam kasus ini.

starblue
sumber
12

Semua orang di sini tampaknya berpikir bahwa mengimplementasikan Runnable adalah cara untuk pergi dan saya tidak benar-benar tidak setuju dengan mereka tetapi ada juga kasus untuk memperluas Thread menurut pendapat saya, sebenarnya Anda telah menunjukkan semacam itu dalam kode Anda.

Jika Anda menerapkan Runnable maka kelas yang mengimplementasikan Runnable tidak memiliki kendali atas nama utas, itu adalah kode panggilan yang dapat mengatur nama utas, seperti:

new Thread(myRunnable,"WhateverNameiFeelLike");

tetapi jika Anda memperluas Thread maka Anda bisa mengelola ini di dalam kelas itu sendiri (seperti dalam contoh Anda, Anda memberi nama utas 'ThreadB'). Dalam hal ini Anda:

A) mungkin memberinya nama yang lebih berguna untuk keperluan debugging

B) memaksa agar nama itu digunakan untuk semua instance kelas itu (kecuali Anda mengabaikan fakta bahwa itu adalah utas dan lakukan yang di atas dengan itu seolah-olah itu adalah Runnable tetapi kita berbicara tentang konvensi di sini dalam hal apa pun sehingga dapat abaikan kemungkinan itu yang saya rasakan).

Misalnya, Anda bahkan dapat mengambil jejak tumpukan kreasi dan menggunakannya sebagai nama utas. Ini mungkin tampak aneh tetapi tergantung pada bagaimana kode Anda terstruktur itu bisa sangat berguna untuk keperluan debugging.

Ini mungkin tampak seperti hal kecil tetapi di mana Anda memiliki aplikasi yang sangat kompleks dengan banyak utas dan tiba-tiba 'telah berhenti' (baik karena kebuntuan atau mungkin karena cacat dalam protokol jaringan yang akan kurang jelas - atau alasan tak berujung lainnya) kemudian mendapatkan tumpukan tumpukan dari Jawa di mana semua utas disebut 'Utas-1', 'Utas-2', 'Utas-3' tidak selalu sangat berguna (tergantung pada bagaimana utas Anda berada terstruktur dan apakah Anda bisa mengetahui mana yang hanya dengan jejak tumpukan mereka - tidak selalu mungkin jika Anda menggunakan grup beberapa utas yang semuanya menjalankan kode yang sama).

Setelah mengatakan bahwa Anda tentu saja dapat juga melakukan hal di atas dengan cara yang umum dengan membuat ekstensi kelas utas yang menetapkan namanya menjadi jejak tumpukan panggilan penciptaannya dan kemudian menggunakannya dengan implementasi Runnable Anda alih-alih kelas java Thread standar (lihat di bawah) tetapi selain jejak tumpukan mungkin ada informasi spesifik lebih konteks yang akan berguna dalam nama utas untuk debugging (referensi ke salah satu dari banyak antrian atau soket yang dapat diproses misalnya dalam hal mana Anda mungkin lebih suka untuk rentangkan Thread secara khusus untuk kasing itu sehingga Anda dapat membuat kompiler memaksa Anda (atau orang lain menggunakan pustaka Anda) untuk menyampaikan info tertentu (mis. antrian / soket yang dimaksud) untuk digunakan dalam nama).

Berikut adalah contoh utas umum dengan jejak tumpukan panggilan sebagai namanya:

public class DebuggableThread extends Thread {
    private static String getStackTrace(String name) {
        Throwable t= new Throwable("DebuggableThread-"+name);
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        PrintStream ps = new PrintStream(os);
        t.printStackTrace(ps);
        return os.toString();
    }

    public DebuggableThread(String name) {
        super(getStackTrace(name));
    }

    public static void main(String[] args) throws Exception {
        System.out.println(new Thread());
        System.out.println(new DebuggableThread("MainTest"));
    }
}

dan inilah contoh output yang membandingkan kedua nama:

Thread[Thread-1,5,main]
Thread[java.lang.Throwable: DebuggableThread-MainTest
    at DebuggableThread.getStackTrace(DebuggableThread.java:6)
    at DebuggableThread.<init>(DebuggableThread.java:14)
    at DebuggableThread.main(DebuggableThread.java:19)
,5,main]
Antonim
sumber
cao apa maksudmu? Anda tidak bisa menggunakan kode Anda di atas selama pelaksanaan utas untuk mendapatkan stacktrace dari penciptaan utas (sebaliknya Anda akan mendapatkan nama yang sederhana atau paling baik susunan jejak peluncuran benang) tetapi dengan subkelas utas Anda dapat melakukan hal itu dan memaksa itu, bahkan memerlukan informasi spesifik konteks lebih lanjut sehingga memberi Anda pemahaman yang lebih konkret tentang topik yang mungkin memiliki masalah.
AntonyM
8
Maksud saya adalah "Jika Anda mengimplementasikan Runnable maka kelas yang mengimplementasikan Runnable tidak memiliki kendali atas nama utas ..." jelas-jelas salah. Kelas yang mengimplementasikan Runnablememang dapat mengontrol nama utas, karena utas yang menjalankan kode menurut definisi utas saat ini (dan kode apa pun yang melewati pemeriksaan keamanan memiliki kendali atas nama utas). Mengingat Anda mengabdikan setengah pos Anda ke "omg, bagaimana dengan nama utas!", Itu sepertinya masalah besar.
cHao
Nama utas? Tidak ada yang menghentikan Anda memperluas kelas utas juga.
RichieHH
11

Runnable karena:

  • Meninggalkan lebih banyak fleksibilitas untuk implementasi Runnable untuk memperluas kelas lain
  • Memisahkan kode dari eksekusi
  • Memungkinkan Anda menjalankan runnable Anda dari Thread Pool, event thread, atau dengan cara lain apa pun di masa depan.

Bahkan jika Anda tidak memerlukan semua ini sekarang, Anda mungkin di masa depan. Karena tidak ada untungnya mengganti Thread, Runnable adalah solusi yang lebih baik.

n13
sumber
11

Karena ini adalah topik yang sangat populer dan jawaban yang baik tersebar di seluruh dan ditangani dengan sangat mendalam, saya merasa dibenarkan untuk mengkompilasi jawaban yang baik dari yang lain menjadi bentuk yang lebih ringkas, sehingga pendatang baru memiliki ikhtisar yang mudah di muka:

  1. Anda biasanya memperluas kelas untuk menambah atau memodifikasi fungsionalitas. Jadi, jika Anda tidak ingin untuk menimpa setiap perilaku Thread , kemudian menggunakan Runnable.

  2. Dalam cahaya yang sama, jika Anda tidak perlu untuk mewarisi metode benang, Anda dapat melakukannya tanpa itu biaya overhead dengan menggunakan Runnable.

  3. Warisan tunggal : Jika Anda memperpanjang Utas Anda tidak dapat memperluas dari kelas lain, jadi jika itu yang perlu Anda lakukan, Anda harus menggunakan Runnable.

  4. Ini adalah desain yang baik untuk logika domain terpisah dari sarana teknis, dalam arti bahwa lebih baik untuk memiliki tugas Runnable mengisolasi Anda tugas dari Anda pelari .

  5. Anda dapat menjalankan objek Runnable yang sama beberapa kali , objek Thread, namun, hanya dapat dimulai sekali. (Mungkin alasannya, mengapa Pelaksana menerima Runnables, tetapi bukan Threads.)

  6. Jika Anda mengembangkan tugas sebagai Runnable, Anda memiliki semua fleksibilitas cara menggunakannya sekarang dan di masa depan . Anda dapat menjalankannya secara bersamaan melalui Pelaksana tetapi juga melalui Utas. Dan Anda masih bisa menggunakan / menyebutnya secara tidak bersamaan dalam utas yang sama seperti jenis / objek biasa lainnya.

  7. Ini membuatnya juga lebih mudah untuk memisahkan tugas-logika dan aspek konkurensi dalam pengujian unit Anda .

  8. Jika Anda tertarik dengan pertanyaan ini, Anda mungkin juga tertarik pada perbedaan antara Callable dan Runnable .

Jorg
sumber
@Pino Ya, Thread itu sendiri juga merupakan Runnable. Namun, jika Anda mengembangkannya hanya untuk menggunakannya sebagai Runnable, apa gunanya? Mengapa tidak menggunakan Runnable polos tanpa semua bagasi. Jadi, saya berpendapat, bahwa jika Anda memperluas Thread, Anda juga akan menjalankannya dengan menggunakan metode start-nya, yang hanya dapat digunakan sekali. Itulah poin yang ingin dibuat Nidhish-Krishnan dalam jawabannya. Perhatikan, tambang itu hanya kompilasi atau ringkasan singkat dari jawaban lain di sini.
Jorg
11

Perbedaan antara Memperluas Thread dan Menerapkan Runnable adalah:

masukkan deskripsi gambar di sini

Raman Gupta
sumber
8

Ini dibahas dalam tutorial Mendefinisikan dan Memulai Thread Oracle :

Manakah dari idiom ini yang harus Anda gunakan? Idiom pertama, yang menggunakan objek Runnable, lebih umum, karena objek Runnable dapat subkelas kelas selain Thread. Idiom kedua lebih mudah digunakan dalam aplikasi sederhana, tetapi dibatasi oleh fakta bahwa kelas tugas Anda harus merupakan turunan dari Thread. Pelajaran ini berfokus pada pendekatan pertama, yang memisahkan tugas Runnable dari objek Thread yang mengeksekusi tugas. Tidak hanya pendekatan ini lebih fleksibel, tetapi juga berlaku untuk API manajemen thread tingkat tinggi yang dibahas kemudian.

Dengan kata lain, implementasi Runnableakan bekerja dalam skenario di mana kelas Anda memperluas kelas selain Thread. Java tidak mendukung multiple inheritance. Selain itu, perluasan Threadtidak akan mungkin dilakukan saat menggunakan beberapa API manajemen utas tingkat tinggi. Satu-satunya skenario di mana perpanjangan Threadlebih disukai adalah dalam aplikasi kecil yang tidak akan mengalami pembaruan di masa depan. Hampir selalu lebih baik untuk diterapkan Runnablekarena lebih fleksibel seiring proyek Anda tumbuh. Perubahan desain tidak akan berdampak besar karena Anda dapat mengimplementasikan banyak antarmuka di java, tetapi hanya memperluas satu kelas.

Sionnach733
sumber
8

Penjelasan paling sederhana adalah dengan mengimplementasikan Runnablekita dapat menetapkan objek yang sama ke beberapa utas dan masing-masing Threadberbagi status dan perilaku objek yang sama.

Sebagai contoh, misalkan ada dua utas, utas1 menempatkan integer dalam array dan utas2 mengambil integer dari array ketika array diisi. Perhatikan bahwa agar thread2 berfungsi, perlu mengetahui status array, apakah thread1 telah mengisinya atau belum.

Menerapkan Runnablememungkinkan Anda untuk memiliki fleksibilitas ini untuk berbagi objek sedangkan extends Threadmembuat Anda membuat objek baru untuk setiap utas karena itu setiap pembaruan yang dilakukan oleh utas1 hilang oleh utas2.

Shababb Karim
sumber
7

Jika saya tidak salah, ini kurang lebih mirip

Apa perbedaan antara antarmuka dan kelas abstrak?

extends menetapkan hubungan " Is A " & antarmuka menyediakan kemampuan " Memiliki ".

Lebih suka mengimplementasikan Runnable :

  1. Jika Anda tidak perlu memperpanjang kelas Thread dan memodifikasi implementasi default Thread API
  2. Jika Anda menjalankan api dan lupakan perintah
  3. Jika Anda sudah memperpanjang kelas lain

Lebih suka " extends Thread ":

  1. Jika Anda harus mengganti metode Thread ini seperti yang tercantum dalam halaman dokumentasi oracle

Secara umum Anda tidak perlu mengganti perilaku Thread. Jadi mengimplementasikan Runnable lebih disukai untuk sebagian besar waktu.

Pada catatan yang berbeda, menggunakan lanjutan ExecutorServiceatau ThreadPoolExecutorServiceAPI memberikan lebih banyak fleksibilitas dan kontrol.

Lihat Pertanyaan SE ini:

ExecutorService vs Casual Thread Spawner

Ravindra babu
sumber
5

Memisahkan kelas Thread dari implementasi Runnable juga menghindari potensi masalah sinkronisasi antara metode thread dan run (). Runnable yang terpisah umumnya memberikan fleksibilitas yang lebih besar dalam cara kode runnable direferensikan dan dieksekusi.

Govula Srinivas
sumber
5

Itulah S dari SOLID : tanggung jawab tunggal.

Sebuah thread mewujudkan konteks yang sedang berjalan (seperti dalam konteks eksekusi: stack frame, id thread, dll.) Dari eksekusi asinkron sepotong kode. Itu bagian dari kode idealnya harus pelaksanaan yang sama, apakah sinkron atau asinkron .

Jika Anda menyatukannya dalam satu implementasi, Anda memberikan objek yang dihasilkan dua penyebab perubahan yang tidak terkait :

  1. penanganan utas dalam aplikasi Anda (mis. menanyakan dan mengubah konteks eksekusi)
  2. algoritma yang diterapkan oleh potongan kode (bagian runnable)

Jika bahasa yang Anda gunakan mendukung kelas parsial atau pewarisan berganda, maka Anda dapat memisahkan masing-masing penyebab dalam kelas supernya sendiri, tetapi itu bermuara pada sama dengan menyusun dua objek, karena set fitur mereka tidak tumpang tindih. Itu untuk teorinya.

Dalam praktiknya, secara umum, suatu program tidak perlu membawa lebih banyak kompleksitas daripada yang diperlukan. Jika Anda memiliki satu utas yang mengerjakan tugas tertentu, tanpa pernah mengubah tugas itu, mungkin tidak ada gunanya membuat kelas tugas terpisah, dan kode Anda tetap lebih sederhana.

Dalam konteks Jawa , karena fasilitas tersebut sudah ada di sana , mungkin lebih mudah untuk memulai langsung dengan Runnablekelas yang berdiri sendiri , dan meneruskan instance mereka ke Thread(atau Executor) instance. Setelah terbiasa dengan pola itu, tidak sulit untuk menggunakan (atau bahkan membaca) daripada case thread runnable yang sederhana.

didierc
sumber
5

Salah satu alasan Anda ingin mengimplementasikan antarmuka daripada memperluas kelas dasar adalah karena Anda telah memperluas beberapa kelas lainnya. Anda hanya dapat memperluas satu kelas, tetapi Anda dapat mengimplementasikan sejumlah antarmuka.

Jika Anda memperpanjang Utas, pada dasarnya Anda mencegah logika Anda dieksekusi oleh utas lain selain 'ini'. Jika Anda hanya ingin beberapa utas untuk mengeksekusi logika Anda, lebih baik menerapkan Runnable.

Nikhil AA
sumber
Ya dengan mengimplementasikan antarmuka Runnable agar bebas menerapkan logika Anda sendiri dengan memperluas kelas apa pun, itulah mengapa Runnable lebih disukai daripada kelas Thread.
Akash5288
5

jika Anda menggunakan runnable Anda dapat menghemat ruang untuk memperluas ke kelas Anda yang lain.

pengguna2771655
sumber
5

Bisakah kita mengunjungi kembali alasan dasar kita ingin kelas kita berperilaku sebagai Thread? Tidak ada alasan sama sekali, kami hanya ingin menjalankan tugas, kemungkinan besar dalam mode asinkron, yang berarti bahwa pelaksanaan tugas harus bercabang dari utas utama kami dan utas utama jika selesai lebih awal, mungkin atau mungkin tidak menunggu untuk jalur bercabang (tugas).

Jika ini adalah seluruh tujuannya, lalu di mana saya melihat kebutuhan Thread khusus. Ini dapat dicapai dengan mengambil RAW Thread dari Thread's System dan menugaskannya tugas kami (mungkin turunan dari kelas kami) dan hanya itu.

Jadi marilah kita mematuhi konsep OOP dan menulis kelas dari jenis yang kita butuhkan. Ada banyak cara untuk melakukan berbagai hal, melakukannya dengan cara yang benar penting.

Kami membutuhkan tugas, jadi tulis definisi tugas yang dapat dijalankan di Thread. Jadi gunakan Runnable.

Ingat selalu implementsdigunakan khusus untuk menanamkan perilaku danextends digunakan untuk memberikan fitur / properti.

Kami tidak menginginkan properti utas, sebaliknya kami ingin kelas kami berperilaku sebagai tugas yang dapat dijalankan.

Dharam
sumber
4

Ya, Jika Anda memanggil panggilan ThreadA, maka tidak perlu memanggil metode mulai dan menjalankan metode adalah panggilan setelah memanggil kelas ThreadA saja. Tetapi jika menggunakan panggilan ThreadB maka perlu perlu utas awal untuk metode menjalankan panggilan. Jika Anda memiliki bantuan lagi, balas saya.

Manoj Kumar
sumber
4

Saya merasa paling berguna untuk menggunakan Runnable untuk semua alasan yang disebutkan, tetapi kadang-kadang saya suka memperpanjang Thread sehingga saya dapat membuat metode menghentikan thread saya sendiri dan menyebutnya langsung pada thread yang telah saya buat.

Tarvaris Jackson
sumber
4

Java tidak mendukung multiple inheritence jadi jika Anda memperluas kelas Thread maka tidak ada kelas lain yang akan diperluas.

Sebagai contoh: Jika Anda membuat applet maka ia harus memperluas kelas Applet jadi di sini satu-satunya cara untuk membuat utas adalah dengan mengimplementasikan antarmuka Runnable

Himanshu Mohta
sumber
4

Runnableadalah antarmuka, sedangkan Threadkelas yang mengimplementasikan antarmuka ini. Dari sudut pandang desain, harus ada pemisahan yang bersih antara bagaimana tugas didefinisikan dan antara bagaimana itu dieksekusi. Yang pertama adalah tanggung jawab Runnalbeimplementasi, dan yang terakhir adalah pekerjaan Threadkelas. Dalam sebagian besar kasus, penerapan Runnableadalah cara yang tepat untuk diikuti.

developer110
sumber
4

Perbedaan antara Thread dan runnable. Jika kita membuat Thread menggunakan kelas Thread maka Jumlah thread sama dengan jumlah objek yang kita buat. Jika kita membuat utas dengan mengimplementasikan antarmuka yang dapat dijalankan maka kita dapat menggunakan objek tunggal untuk membuat beberapa utas. Jadi satu objek dibagi oleh beberapa Utas. Jadi itu akan memakan lebih sedikit memori

Jadi tergantung persyaratannya jika data kita tidak senstive. Jadi itu bisa dibagi antara beberapa Thread kita bisa menggunakan antarmuka Runnable.

Rohit Chugh
sumber
4

Menambahkan dua sen saya di sini - Selalu sedapat mungkin gunakan implements Runnable. Di bawah ini adalah dua peringatan mengapa Anda tidak harus menggunakan extends Threads

  1. Idealnya Anda tidak perlu memperluas kelas Thread; yang Threadkelas harus dibuat final. Setidaknya seperti metode thread.getId(). Lihat diskusi ini untuk bug yang terkait dengan extending Threads.

  2. Mereka yang suka memecahkan teka-teki dapat melihat efek samping lain dari memperluas Thread. Kode di bawah ini akan mencetak kode yang tidak terjangkau ketika tidak ada yang memberi tahu mereka.

Silakan lihat http://pastebin.com/BjKNNs2G .

public class WaitPuzzle {

    public static void main(String[] args) throws InterruptedException {
        DoNothing doNothing = new DoNothing();
        new WaitForever(doNothing).start();
        new WaitForever(doNothing).start();
        new WaitForever(doNothing).start();
        Thread.sleep(100);
        doNothing.start();
        while(true) {
            Thread.sleep(10);
        }
    }


    static class WaitForever extends  Thread {

        private DoNothing doNothing;

        public WaitForever(DoNothing doNothing) {
            this.doNothing =  doNothing;
        }

        @Override
        public void run() {
            synchronized (doNothing) {
                try {
                    doNothing.wait(); // will wait forever here as nobody notifies here
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("Unreachable Code");
            }
        }
    }

    static class DoNothing extends Thread {

        @Override
        public void run() {
            System.out.println("Do Nothing ");
        }
    } 
}
veritas
sumber