Java'da “Runnable” ve “Thread'ı genişletir”


2118

Ne zaman Java iş parçacıkları ile geçirdim, iş parçacığı yazmak için bu iki yol buldum:

İle implements Runnable:

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

Veya extends Thread:

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

Bu iki kod bloğunda önemli bir fark var mı?


57
Bu soru için teşekkürler, cevaplar yaşadığım pek çok yanlış anlamadan kurtuldu. SO var önce Java iş parçacığı yapmak için doğru yolu içine baktı ve orada bir çok yanlış bilgi / modası geçmiş bilgi vardı.
James McMahon

5
İpliği uzatmak isteyebileceğiniz bir neden var ( ancak bunu önermiyorum ), önleyici olarak ele alabilirsiniz interrupt(). Yine, bir fikir, doğru durumda yararlı olabilir, ancak bunu önermiyorum.
bestsss

Lütfen güzel açıklanmış cevaba da bakınız: stackoverflow.com/q/5562720/285594

@bestsss, interrupt () işlemiyle ilgili ne anlama gelebileceğinizi bulmaya çalışıyorum. Yöntemi geçersiz kılmaya mı çalışıyorsunuz?
Bob Cross

8
Evet. Kod uyarınca, A Sınıfı sınıfı herhangi bir sınıfı genişletebilirken B sınıfı A sınıfı başka bir sınıfı genişletemez
mani deepak

Yanıtlar:


1672

Evet: Runnablebunu yapmanın tercih edilen yolu IMO'dur. İş parçacığının davranışını gerçekten uzmanlaştırmıyorsunuz. Sadece kaçması için bir şey veriyorsun. Bu, kompozisyonun felsefi olarak "daha saf" bir yol olduğu anlamına gelir .

Gelen pratik açıdan, bu uygulamaya anlamına gelir Runnableve başka bir sınıftan uzatmak de.


151
Kesinlikle, iyi koymak. İleti dizisini genişleterek üzerine nasıl bir davranış yazmaya çalışıyoruz? Çoğu insanın herhangi bir davranışın üzerine yazmaya çalışmadığını, ancak Thread davranışını kullanmaya çalıştığını iddia ediyorum.
hooknc

87
Bir yan yorum olarak, bir İş Parçasını başlatır ve start () yöntemini çağırmazsanız, Java <5'de bir bellek sızıntısı oluşturursunuz (bu Runnables ile olmaz): stackoverflow.com/questions/107823/…
Nacho Coloma

25
Runnable'ın küçük bir avantajı, belirli durumlarda iş parçacığını umursamıyorsanız veya kullanmak istemiyorsanız ve sadece kodu yürütmek istiyorsanız, sadece run () çağırma seçeneğinizin olmasıdır. ör. (çok elle yıkama) if (numberCores > 4) myExecutor.excute(myRunnable); else myRunnable.run()
user949300

10
@ user949300 ile de yapabilirsiniz extends Threadve eğer diş istemiyorsanız neden uygulamak bile Runnable...
m0skit0

30
Sierra ve Bates'ü yorumlamak için Runnable'ı uygulamanın önemli bir yararı, "işi" mimari olarak "koşucu" dan ayırmanızdır.
8bitjunkie

569

tl; dr: Runnable'ı uygulamak daha iyidir. Ancak, uyarı önemlidir

Genel olarak, işinizi sadece eşzamanlılık seçiminizle birlikte gevşek bir şekilde tutmanıza izin verdiği için Runnabledeğil, böyle bir şey kullanmanızı öneririm Thread. Örneğin, a kullanırsanız Runnableve daha sonra bunun aslında kendisinin gerektirmediğine karar verirseniz Thread, sadece threadA.run () öğesini arayabilirsiniz.

Dikkat: Buralarda, ham iplik kullanımından kesinlikle vazgeçiyorum. Ben Callables ve FutureTasks kullanımını tercih (Javadoc Gönderen: "İptal edilebilir bir asenkron hesaplama"). Zaman aşımlarının entegrasyonu, uygun iptal ve modern eşzamanlılık desteğinin iş parçacığı havuzu benim için ham iş parçacıkları yığınlarından çok daha yararlıdır.

Takip: Runnables'ı (en rahat olduğunuz şeyse) kullanmanıza ve hala modern eşzamanlılık araçlarından yararlanmanıza izin veren bir FutureTaskkurucu var. Javadoc'u alıntılamak için :

Belirli bir sonuca ihtiyacınız yoksa, formun yapılarını kullanmayı düşünün:

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

Yani, onların runnableyerine sizin threadA, biz aşağıdaki olsun:

new FutureTask<Object>(threadA, null)

Runnables'a daha yakın kalmanızı sağlayan başka bir seçenek de ThreadPoolExecutor'tur . "Verilen görevi gelecekte bir zamanda" yürütmek için bir Runnable'ı iletmek için execute yöntemini kullanabilirsiniz .

Bir iş parçacığı havuzu kullanmayı denemek isterseniz, yukarıdaki kod parçası aşağıdaki gibi bir şey olacaktır ( Executors.newCachedThreadPool () fabrika yöntemini kullanarak ):

ExecutorService es = Executors.newCachedThreadPool();
es.execute(new ThreadA());

40
Bu IMHO'nun kabul ettiği cevaptan daha iyidir. Bir şey: sahip olduğunuz kod snippet'i yürütücüyü kapatmıyor ve insanların bunu yanlış anladıkları milyonlarca soru görüyorum ve her görev oluşturmak istediklerinde yeni bir Yürütücü oluşturdum. esstatik (veya enjekte edilmiş) bir alan olarak daha iyi olur, böylece yalnızca bir kez oluşturulur.
artbristol

7
@artbristol, teşekkürler! Yeni Yürütücüye katılmıyorum (kodumuzda önerdiklerinizi yapıyoruz). Orijinal cevabı yazarken, orijinal parçaya benzer minimum kod yazmaya çalışıyordum. Bu cevapların birçok okuyucusunun bu soruları atlama noktası olarak kullanmasını ummalıyız. Javadoc'un yerine yazmaya çalışmıyorum. Bunun için pazarlama materyali etkili bir şekilde yazıyorum: Bu yöntemi beğendiyseniz, sunmamız gereken diğer tüm harika şeyleri görmelisiniz ...!
Bob Cross

3
Bu konuda yorum yapmak için biraz geç kaldığımı biliyorum, ancak FutureTaskdoğrudan uğraşmak genellikle yapmak istediğiniz şey değildir. ExecutorServices uygun düzgün yaratacak Futuresizin için submitbir Runnable/ Callableonlara. Aynı şekilde ScheduledExecutorServices ve ScheduledFuturene zaman schedulea Runnable/ Callable.
Powerlord

3
@Powerlord, niyetim OP'lerle mümkün olan en yakın kod parçalarını yapmaktı. Yeni FutureTask'ın optimal olmadığını ancak açıklama amacıyla açık olduğunu kabul ediyorum.
Bob Cross

257

Hikayeden çıkarılacak ders:

Yalnızca bazı davranışları geçersiz kılmak istiyorsanız devralın.

Ya da daha doğrusu şu şekilde okunmalıdır:

Daha az miras, daha fazla arayüz.


1
Eşzamanlı çalışan bir Object yapmaya başlarsanız bu her zaman soru olmalıdır! İş Parçacığı Nesnesi işlevlerine bile ihtiyacınız var mı?
Liebertee

4
İş parçacığından devralınırken, neredeyse her zaman run()yöntemin davranışını geçersiz kılmak ister .
Warren Dew

1
A yöntemini java.lang.Threadgeçersiz kılarak a'nın davranışını geçersiz kılamazsınız run(). Bu durumda start()sanırım yöntemi geçersiz kılmanız gerekir . Normalde java.lang.Thread, yürütme bloğunuzu run()yönteme enjekte ederek davranışını yeniden kullanırsınız .
sura2k

Kalıtım sadece bazı davranışları geçersiz kılmak için değil, aynı zamanda ortak davranışları kullanmaktır. Ve tam tersi, daha fazla geçersiz kılma, daha kötü hiyerarşi.
peja

232

Pek çok iyi Yanıt, bu konuda daha fazla eklemek istiyorum. Bu anlamanıza yardımcı olacaktır Extending v/s Implementing Thread.
İki sınıf dosyasını çok yakından bağlar ve kodla başa çıkmak oldukça zor olabilir.

Her iki yaklaşım da aynı işi yapar ancak bazı farklılıklar vardır.
En yaygın fark

  1. Thread sınıfını genişlettiğinizde, bundan sonra istediğiniz diğer sınıfı genişletemezsiniz. (Bildiğiniz gibi, Java birden fazla sınıfın miras alınmasına izin vermez).
  2. Runnable'ı uyguladığınızda, sınıfınızın gelecekte veya şimdi başka bir sınıfı genişletmesi için yer kazanabilirsiniz.

Ancak, Runnable'ı uygulamak ve Thread'ı genişletmek arasındaki önemli bir fark ,
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.

Aşağıdaki örnek daha net bir şekilde anlamanıza yardımcı olacaktır

//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();
 }
}

Yukarıdaki programın çıktısı.

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

Runnable arabirimi yaklaşımında, bir sınıfın yalnızca bir örneği oluşturulur ve farklı iş parçacıkları tarafından paylaşılır. Böylece sayacın değeri her iş parçacığı erişimi için artırılır.

Oysa Thread sınıf yaklaşımı, her thread erişimi için ayrı bir örnek oluşturmanız gerekir. Bu nedenle, her sınıf örneği için farklı bellek ayrılır ve her birinin ayrı sayacı vardır, değer aynı kalır, yani nesne başvurusunun hiçbiri aynı olmadığından artış olmaz.

Runnable ne zaman kullanılır?
İş parçacığı grubundan aynı kaynaklara erişmek istediğinizde Runnable arabirimini kullanın. Burada Thread sınıfını kullanmaktan kaçının, çünkü birden fazla nesne oluşturma daha fazla bellek tüketir ve büyük bir performans yükü haline gelir.

Runnable'ı uygulayan bir sınıf bir iş parçacığı değil, sadece bir sınıftır. Bir Runnable'ın İş Parçacığı olması için, iş parçacığının bir örneğini oluşturmanız ve kendisini hedef olarak geçirmeniz gerekir.

Çoğu durumda, yalnızca run()yöntemi geçersiz kılmayı planlıyorsanız ve başka bir Thread yöntemi yoksa Runnable arabirimi kullanılmalıdır . Bu önemlidir, çünkü programcı sınıfın temel davranışını değiştirmek veya geliştirmek istemiyorsa sınıflar alt sınıflara ayrılmamalıdır.

Bir üst sınıfı genişletmeye ihtiyaç duyulduğunda, Runnable arabirimini uygulamak Thread sınıfını kullanmaktan daha uygundur. Çünkü bir iş parçacığı oluşturmak için Runnable arabirimini uygularken başka bir sınıfı genişletebiliriz.

Umarım bu yardımcı olacak!


40
Kodunuz yanlış bir şekilde yanlış. Yani, ne yaptığını yapar, ama göstermek istediğini yapmaz.
zEro

38
Açıklığa kavuşturmak için: çalıştırılabilir durum için, aynı ImplementsRunnable örneğini birden çok iş parçacığı başlatmak için kullandınız, iş parçacığı durumu için de açıkça gösterdiğiniz davranışa yol açan farklı ExtendsThread örnekleri oluşturuyorsunuz. Ana yönteminizin 2. yarısı şöyle olmalıdır: Daha 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(); açık mı?
zEro

19
Niyetinizi henüz anlamadım, ama benim açımdan, ExtendsThread'in birden fazla örneğini oluşturursanız - hepsinin 1'i (gösterdiğiniz gibi) döndüreceği oldu. Runnable için aynı sonuçları orada aynı şeyi yaparak, yani birden çok ImplementsRunnable örneği oluşturarak elde edebilirsiniz.
zEro

3
@zEro Merhaba, ben gelecekten geliyorum. Kod sürümünüzde de Threadartış olduğu göz önüne alındığında , ifade by extending Thread, each of your threads has a unique object associated with it, whereas implementing Runnable, many threads can share the same object instanceyanlış mı? Değilse, bunu gösteren bir vaka nedir?
Evil Çamaşır Makinesi

4
Burada yayınlanan kod yanıltıcıdır ve baş ağrısına yol açabilir. @ZEro'ya biraz temizlediği için teşekkürler.
Nikos

78

Şaşırmış olduğum bir şey henüz belirtilmedi, uygulamanın Runnablesınıfınızı daha esnek hale getirmesi .

İş parçacığını genişletirseniz, yaptığınız işlem her zaman bir iş parçacığında olur. Bununla birlikte, eğer uygularsanız, Runnablebunun olması gerekmez. Bir iş parçacığında çalıştırabilir veya bir tür yürütücü hizmetine iletebilir veya tek bir iş parçacıklı uygulama içinde bir görev olarak iletebilirsiniz (belki daha sonra çalıştırılabilir, ancak aynı iş parçacığı içinde). Seçenekler sadece Runnablekendinizi bağlarsanız kullanmaktan çok daha açıktır Thread.


6
Eh, aslında bir ile aynı şeyi yapabilir Threadçok çünkü nesne Thread implements Runnable... ;-) Ama "hissettiğini daha iyi" bir bu şeyler yapıyor Runnablebir onları yaparken daha Thread!
siegi

7
Doğru, ancak Threadihtiyacınız olmayan ve çoğu durumda istemediğiniz çok fazla şey ekler. Gerçekte yaptığınız şeyle eşleşen arayüzü uygulamak her zaman daha iyidir.
Herms

74

Başka bir sınıf uygulamak veya genişletmek istiyorsanız Runnablearabirim en çok tercih edilir, aksi takdirde başka bir sınıfın genişletilmesini veya uygulanmasını istemezseniz Threadsınıf tercih edilir.

En yaygın fark

resim açıklamasını buraya girin

Ne zaman extends Threadsınıf, size gerekli görülen diğer sınıfını uzatamaz bundan sonra. (Bildiğiniz gibi, Java birden fazla sınıfın miras alınmasına izin vermez).

Siz implements Runnablene zaman isterseniz , sınıfınızın gelecekte veya şimdi başka bir sınıfı genişletmesi için yer kazanabilirsiniz.

  • Java birden fazla devralmayı desteklemez, yani Java'da yalnızca bir sınıfı genişletebilirsiniz, böylece Thread sınıfını genişlettikten sonra şansınızı kaybedersiniz ve Java'da başka bir sınıfı genişletemez veya devralmazsınız.

  • Nesne yönelimli programlamada, bir sınıfı genişletmek genellikle yeni işlevler eklemek ve davranışları değiştirmek veya geliştirmek anlamına gelir. Thread üzerinde herhangi bir değişiklik yapmıyorsak, Runnable arayüzünü kullanın.

  • Çalıştırılabilir arabirim, düz İş parçacığı veya Yürütücüler veya başka herhangi bir yolla yürütülebilen bir Görevi temsil eder. bu yüzden Görevin İş Parçacığı'ndan Çalıştırılabilir olarak mantıksal olarak ayrılması iyi bir tasarım kararıdır.

  • Görevi Runnable olarak ayırmak, görevi yeniden kullanabileceğimiz anlamına gelir ve ayrıca farklı yollardan yürütme özgürlüğüne sahiptir. çünkü bir Konuyu tamamladıktan sonra yeniden başlatamazsınız. tekrar Runnable vs Görev için iplik, Runnable kazanır.

  • Java tasarımcısı bunu tanır ve bu yüzden Yürütücüler Runnable'ı Görev olarak kabul eder ve bu görevi yürüten işçi iş parçacığına sahiptir.

  • Tüm İş Parçacığı yöntemlerinin devralınması, yalnızca Runnable ile kolayca yapılabilecek bir Görevi temsil etmek için ek yüktür.

Javarevisited.blogspot.com izniyle

Bunlar, Java'da Thread ve Runnable arasındaki önemli farklardan bazılarıydı. Thread vs Runnable'da başka farklılıklar biliyorsanız, lütfen yorumlarla paylaşın. Bu senaryo için şahsen İplik üzerinden Runnable kullanıyorum ve sizin ihtiyacınıza göre Runnable veya Callable arabirimini kullanmanızı öneririm.

Ancak, önemli fark.

Ne zaman size extends Threadsınıfı, senin dişin her onunla benzersiz nesne ve ortağı oluşturur. Siz implements Runnable, aynı nesneyi birden çok iş parçacığıyla paylaşır.


73

Aslında O karşılaştırmak akıllıca değil Runnableve Threadbirbirleriyle.

Bu ikisi, tıpkı Wheel and Enginemotorlu taşıt ilişkisi gibi çoklu iplik geçirmede bağımlılık ve ilişkiye sahiptir .

Şunu söyleyebilirim ki, iki adımda çoklu iş parçacığı için tek bir yol var. Ne demek istediğimi açıklayayım.

Runnable:
uygularken interface Runnablebunu sizsiniz şey yaratmak demektir run ablefarklı bir iş parçacığı. Şimdi bir iş parçacığının içinde çalışabilecek bir şey oluşturmak (iş parçacığının içinde çalıştırılabilir), bir İş parçacığı oluşturmak anlamına gelmez.
Yani sınıf MyRunnable, void runmetodu olan sıradan bir sınıftan başka bir şey değildir . Ve bu nesneler, sadece runçağrıldığında normal olarak yürütülecek bir yöntemle bazı sıradan nesneler olacaktır. (nesneyi bir iş parçacığında geçirmediğimiz sürece).

Thread:,
class Thread Aslında start()yöntemi ile çoklu iş parçacığı sağlayan yeni bir iş parçacığı başlatmak için yeteneği ile çok özel bir sınıf söyleyebilirim .

Neden karşılaştırmak akıllıca değil?
Çünkü çoklu iş parçacığı için her ikisine de ihtiyacımız var.

Multi-threading için iki şeye ihtiyacımız var:

  • Bir İş Parçasının içinde çalışabilecek bir şey (Çalıştırılabilir).
  • Yeni bir Konu (Konu) başlatabilecek bir şey.

Bu yüzden teknik ve teorik olarak her ikisi de bir ipliği başlatmak için gereklidir, biri çalışır ve biri çalışır ( Wheel and Enginemotorlu taşıt gibi).

Bu nedenle, bir iş parçacığını MyRunnablebir örneğine geçirmeniz gerekmiyor Thread.

Ama sadece bir class Threadsınıf oluşturmak ve çalıştırmak mümkündür çünkü Sınıf Threaduygular, Runnableböylece hepimiz Threadbir Runnableiç olduğunu da biliyoruz .

Son olarak Threadve Runnablerakip veya değiştirme değil çoklu kullanım için birbirini tamamlar.


3
Kesinlikle! Bu kabul edilen cevap olmalı. BTW Sorunun düzenlendiğini ve ThreadAartık bir anlamı olmadığını düşünüyorum
idelvall

kabul edilen cevap çok daha fazla temsilci olduğunu yanıt için teşekkürler @idelvall
Saif

En iyi cevap! Teşekkürler!
MichaelYe

44

Sen Runnable uygulamak gerekir, ancak daha yüksek Java 5 çalışan veya alıyorsanız, bunu başlatmak olmamalı new Threadancak kullanmak ExecutorService yerine. Ayrıntılar için bkz: Java'da basit iş parçacığı nasıl uygulanır .


5
Ben sadece tek bir iş parçacığı başlatmak istiyorsanız ExecutorService bu yararlı olacağını düşünmek olmaz.
Powerlord

1
Ne öğrendim kimden biri artık genel olarak kendi başına bir iş parçacığı başlamamalıdır, çünkü bu yürütme hizmetine bırakarak çok daha kontrol edilebilir yapar (mesela, iplik askıya bekliyor). Ayrıca, soruda tek bir iş parçacığıyla ilgili olduğunu ima eden hiçbir şey göremiyorum.
Fabian Steeg

4
Tek bir iş parçacığı olacağını aprior tanıyorsak, herhangi bir çoklu iş parçacığı kullanmanın anlamı nedir. Diyelim ki birden fazla iş parçacığımız var ve bu cevap değerli.
zEro

1
@zEro Yalnızca bir Event Dispatch Thread olmasının bir nedeni olduğundan eminim. Tek bir konu olması en iyisi, ancak muhtemelen birden çok olması iyi değildi şüpheli tek durumdur.
porcoesphino

33

Ben bir uzman değilim, ama uzatmak yerine Runnable uygulamak için bir neden düşünebilirsiniz Konu: Java sadece tek bir miras destekler, böylece sadece bir sınıfı genişletebilirsiniz.

Düzenleme: Bu aslında "Bir arayüz uygulamak daha az kaynak gerektirir." Dedi. ancak her iki şekilde de yeni bir Thread örneği oluşturmanız gerekir, bu yüzden bu yanlıştı.


Runnable'da şebeke araması yapamıyoruz, değil mi? Ben android.os.NetworkOnMainThreadException yaşıyorum. Ama iş parçacığı kullanarak ağ aramaları yapabilirsiniz. Yanlışım varsa lütfen düzelt.
Nabeel Thobani

@NabeelThobani Normal Java umursamıyor, ancak Android'in yaptığı gibi geliyor. Yine de Android'e yeterince tanıdık değilim.
Powerlord

1
@NabeelThobani Tabii ki yapabilirsiniz. Muhtemelen Runnable'ınızla bir iş parçacığı oluşturmuyorsunuz.
m0skit0

20

Üçüncü bir yol olduğunu söyleyebilirim:

public class Something {

    public void justAnotherMethod() { ... }

}

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

Belki de bu, Javascript ve Actionscript 3'ün son yoğun kullanımımdan biraz etkileniyor, ancak bu şekilde sınıfınızın gibi oldukça belirsiz bir arayüz uygulamasına gerek yok Runnable.


38
Bu gerçekten üçüncü bir yol değil. Runnable'ı hala anonim olarak uyguluyorsun.
Don Roby

2
@Don Roby: Hangisi farklı. Genellikle kullanışlıdır ve içeren sınıf / yöntemdeki alanları ve son yerel değişkenleri kullanabilirsiniz.
Bart van Heukelom

6
@BartvanHeukelom Uygun, ama farklı değil. Bunu iç sınıflar, yerel sınıflar ve lambda ifadeleri gibi her tür iç içe sınıfla yapabilirsiniz.
xehpuk

18

Java 8'in piyasaya sürülmesiyle, şimdi üçüncü bir seçenek var.

Runnablea, işlevsel bir arayüz bunun örnekleri lambda ifadeleri veya yöntem referanslarla yaratılabilmesidir araçlar.

Örneğin aşağıdakilerle değiştirilebilir:

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

veya bir ExecutorServiceve yöntem başvurusu kullanmak istiyorsanız :

executor.execute(runner::run)

Bunlar sadece çok daha kısa sizin örneklerden değil, ama aynı zamanda kullanan diğer cevaplar belirtilen avantajların birçok ile gelen Runnableüzerinde Threadböyle tek sorumluluk ve parçacığının davranışını uzmanlaşmış değil çünkü kompozisyon kullanmak gibi. Bu şekilde ayrıca Runnable, örneklerde yaptığınız gibi ihtiyacınız olan tek şey ekstra bir sınıf oluşturmayı da önler .


Bu cevabın açıklanması gerekiyor. Biraz kafa karıştırdıktan sonra () -> {}, birinin ihtiyaç duyduğu özel mantığı temsil etmesi gerektiği sonucuna varıyorum. Yani daha iyi söylenirdi () -> { /* Code here */ }?
ToolmakerSteve

17

Bir arabirim oluşturmak, kodunuz ve iş parçacıklarının uygulanması arasında daha temiz bir ayrım sağlar, bu nedenle bu durumda Runnable'ı uygulamayı tercih ederim.


12

Buradaki herkes Runnable'ın uygulanmasının bir yol olduğunu düşünüyor gibi görünüyor ve ben gerçekten onlarla aynı fikirde değilim ama benim görüşüme göre Thread genişletmek için bir dava da var, aslında kodunuzda bir çeşit gösterdiniz.

Runnable uygularsanız, Runnable uygulayan sınıfın iş parçacığı adı üzerinde hiçbir denetimi yoktur, iş parçacığı adını ayarlayabilen arama kodudur, şöyle:

new Thread(myRunnable,"WhateverNameiFeelLike");

ancak Thread'ı genişletirseniz, bunu sınıfın kendisinde yönetirsiniz (örneğin örneğinizde olduğu gibi 'ThreadB' iş parçacığını adlandırırsınız). Bu durumda:

A) hata ayıklama amacıyla daha yararlı bir ad verebilir

B) bu ismin bu sınıfın tüm örnekleri için kullanılmasını zorunlu kılıyor (eğer bir iş parçacığı olduğu gerçeğini görmezden gelmezseniz ve yukarıdakini Runnablemış gibi yaparsanız, ancak burada her durumda konvansiyondan bahsediyoruz. hissettiğim olasılığı görmezden gel).

Örneğin, yaratımının yığın izlemesini alabilir ve bunu iş parçacığı adı olarak kullanabilirsiniz. Bu tuhaf görünebilir, ancak kodunuzun nasıl yapılandırıldığına bağlı olarak hata ayıklama amaçları için çok yararlı olabilir.

Bu küçük bir şey gibi görünebilir, ancak çok fazla iş parçacığına sahip çok karmaşık bir uygulamanız olduğu ve ani şeylerin 'durduğu' (ya kilitlenme nedenlerinden dolayı veya muhtemelen daha az olan bir ağ protokolündeki bir kusur nedeniyle) bariz - ya da başka sonsuz nedenlerle), daha sonra tüm iş parçacıklarına 'İş Parçacığı-1', 'İş Parçacığı-2', 'İş Parçacığı-3' adı verilen bir yığın dökümü almak her zaman çok yararlı değildir (iş parçacıklarınızın nasıl olduğuna bağlıdır) yapılandırılmış ve hangisinin sadece yığın izlemesiyle hangisinin yararlı olduğunu anlayabiliyor musunuz - hepsi aynı kodu çalıştıran birden çok iş parçacığı grubu kullanıyorsanız her zaman mümkün değildir).

Elbette yukarıdakileri, kendi oluşturma çağrısının yığın izine ayarlayan thread sınıfının bir uzantısını oluşturarak ve ardından bunu standart java Thread sınıfı yerine Runnable uygulamalarınızla kullanarak genel bir şekilde yapabileceğinizi söyledikten sonra (aşağıya bakın) ancak yığın izlemesine ek olarak, hata ayıklama için iş parçacığı adında yararlı olabilecek daha fazla içeriğe özgü bilgi olabilir (örneğin, işleyebileceği birçok kuyruk veya soketten birine yapılan bir başvuru, bu durumda tercih edebilirsiniz Derleyicinin sizi (veya kitaplıklarınızı kullanan diğerlerini) belirli bilgileri (örneğin söz konusu kuyruk / soket) adda kullanılmaya zorlamaya zorlayabilmesi için özel olarak Konuya göre genişletin.

İşte adı olarak çağıran yığın izlemesine sahip genel iş parçacığına bir örnek:

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"));
    }
}

ve işte iki ismi karşılaştıran bir çıktı örneği:

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]

cHao ne demek istiyorsun? İş parçacığının yürütülmesi sırasında yukarıdaki kodunuzu, iş parçacığı oluşturma işleminin yığın izini elde etmek için kullanamazsınız (bunun yerine basit bir ad veya en iyi şekilde iş parçacığının başlatılmasının yığın izini alırsınız), ancak alt sınıflama iş parçacığını tam olarak bunu yapabilir ve zorlayabilirsiniz içeriğe özgü daha fazla bilgi gerektirerek, tam olarak hangi ipliğin bir sorun yaşayabileceğine dair daha somut bir anlayışa sahip olmanızı sağlar.
AntonyM

8
Demek istediğim, "Runnable'ı uygularsanız, Runnable'ı uygulayan sınıfın iş parçacığı adı üzerinde hiçbir denetimi yoktur ..." patentli olarak yanlıştır. RunnableKodu çalıştıran iş parçacığı, geçerli iş parçacığı (ve güvenlik denetimlerini geçen herhangi bir kod iş parçacığı adları üzerinde denetime sahip) tanımlandığı için, bir sınıf uygulama gerçekten iş parçacığı adını denetleyebilir . Gönderinizin yarısını "omg, konu başlıkları hakkında!" Olarak ayırdığınızı düşünürsek, bu büyük bir anlaşma gibi görünüyor.
cHao

İş parçacığı adı? İş parçacığı sınıfını da genişletmenizi sağlayan hiçbir şey yok.
RichieHH

11

Çalıştırılabilir çünkü:

  • Runnable uygulamasının başka bir sınıfı genişletmesi için daha fazla esneklik bırakır
  • Kodu yürütmeden ayırır
  • Runnable'ınızı bir İş Parçacığı Havuzundan, olay iş parçacığından veya gelecekte başka bir şekilde çalıştırmanıza olanak sağlar.

Şimdi bunlara ihtiyacınız olmasa bile, gelecekte de olabilirsiniz. İpliği geçersiz kılmanın hiçbir faydası olmadığından Runnable daha iyi bir çözümdür.


11

Bu çok popüler bir konu olduğu ve iyi yanıtların her tarafa yayıldığı ve büyük bir derinlikle ele alındığı için, diğerlerinin iyi cevaplarını daha özlü bir formda derlemenin haklı olduğunu hissettim, bu nedenle yeni gelenlerin açık bir bakış açısı var:

  1. Genellikle bir işlevsellik eklemek veya değiştirmek için bir sınıfı genişletirsiniz. Yani, sen istemiyorsan için üzerine herhangi Konu davranışı , ardından Runnable kullanın.

  2. Aynı ışığında, ihtiyacınız yoksa hiç devralır iplik yöntemlerle, bunu yapmadan yapabilirsiniz yükü Runnable kullanarak.

  3. Tek miras : Thread'ı başka bir sınıftan uzatamazsanız, bu yüzden yapmanız gereken buysa, Runnable'ı kullanmanız gerekir.

  4. O anlamda bir Runnable görevi olması daha iyidir, teknik araçlarla ayrı alanı mantığına iyi tasarım izole sizin gelen görevi sizin koşucu .

  5. Şunları yapabilirsiniz yürütmek aynı Runnable nesne birden çok kez sadece bir kez başlatılabilir, ancak, bir iş parçacığı bir nesneye. (Belki de nedeni, Yürütücülerin Runnable'ları kabul etmesidir, ancak Konuları kabul etmez.)

  6. Görevinizi Runnable olarak geliştirirseniz, şimdi ve gelecekte nasıl kullanacağınız konusunda tüm esnekliklere sahip olursunuz . Yürütücüler aracılığıyla aynı zamanda iş parçacığı üzerinden de çalıştırabilirsiniz. Ayrıca, diğer tüm normal türler / nesneler gibi aynı iş parçacığı içinde aynı anda kullanabilir / çağırabilirsiniz.

  7. Bu kadar da kolaylaştırır ayrı görev mantık ve eşzamanlılık içinde yönleriyle sizin birim testler .

  8. Bu soru ile ilgileniyorsanız, Callable ve Runnable arasındaki fark da ilginizi çekebilir .


@Pino Evet, İş parçacığının kendisi de bir Runnable. Ancak, sadece Runnable olarak kullanmak için genişletirseniz, amaç nedir? Neden sadece bagaj olmadan düz bir Runnable kullanmıyorsunuz? Yani, Thread'ı genişletirseniz, sadece bir kez kullanılabilen start yöntemini kullanarak da uygulayacağınızı iddia ediyorum. Nidhish-Krishnan'ın cevabında vermek istediği nokta budur. Unutmayın ki, benimki buradaki diğer cevapların sadece bir derlemesi veya kısa bir özeti.
Jörg

11

İş Parçasının Genişletilmesi ve Uygulanabilir Çalıştırılması arasındaki fark:

resim açıklamasını buraya girin


8

Bu, Oracle'ın İş Parçacığı Tanımlama ve Başlatma eğiticisinde ele alınmıştır:

Aşağıdaki deyimlerden hangilerini kullanmalısınız? Runnable nesnesini kullanan ilk deyim daha geneldir, çünkü Runnable nesnesi Thread dışında bir sınıfı alt sınıflandırabilir. İkinci deyimin basit uygulamalarda kullanımı daha kolaydır, ancak görev sınıfınızın İş parçacığının soyundan gelmesi gerekir. Bu ders, Runnable görevini görevi yürüten Thread nesnesinden ayıran ilk yaklaşıma odaklanır. Bu yaklaşım sadece daha esnek olmakla kalmaz, aynı zamanda daha sonra ele alınacak üst düzey iş parçacığı yönetimi API'leri için de geçerlidir.

Başka bir deyişle, uygulama Runnable, sınıfınızın başka bir sınıfı genişlettiği senaryolarda çalışacaktır Thread. Java, birden fazla kalıtımı desteklemez. Ayrıca, Threadbazı üst düzey iş parçacığı yönetimi API'lerini kullanırken genişletmek mümkün olmayacaktır. Genişletmenin Threadtercih edildiği tek senaryo , gelecekte güncellemelere tabi olmayacak küçük bir uygulamadadır. RunnableProjeniz büyüdükçe daha esnek olduğu için uygulamak neredeyse her zaman daha iyidir . Java'da birçok arabirimi uygulayabileceğiniz için tasarım değişikliğinin büyük bir etkisi olmaz, ancak yalnızca bir sınıfı genişletir.


8

En basit açıklama Runnable, aynı nesneyi birden çok iş parçacığına atayabileceğimiz ve her birininThread aynı nesne durumlarını ve davranışlarını paylaştığı şeklinde olacaktır.

Örneğin, iki bağ, orada varsayalım thread1 bir dizi ve koyar bir tamsayıdır thread2 dizi doldurulur, diziye arasındaki tam sayıları alır. Sırayla söz konusu Bildirimi thread2 onu işe, ister dizinin durumunu bilmesi gerekir thread1 o doldurdu veya edilmemiştir.

Uygulama Runnable, nesneyi paylaşmak için bu esnekliğe sahip olmanızı sağlarken extends Thread, her bir iş parçacığı için yeni nesneler oluşturmanızı sağlar, bu nedenle iş parçacığı1 tarafından yapılan tüm güncelleştirmeler iş parçacığı2'ye kaybolur.


7

Eğer yanılmıyorsam, aşağı yukarı

Bir arayüz ile soyut sınıf arasındaki fark nedir?

kurar uzanır " Is A " ilişkisi & arayüz sağlar " Has bir " yeteneği sağlar.

Runnable aletlerini tercih et :

  1. Thread sınıfını genişletmeniz ve Thread API varsayılan uygulamasını değiştirmeniz gerekmiyorsa
  2. Yangın ve unut komutunu uyguluyorsanız
  3. Zaten başka bir sınıfı genişletiyorsanız

" Konuyu uzatır " seçeneğini tercih edin :

  1. Oracle dokümantasyon sayfasında listelendiği gibi bu Konu yöntemlerinden herhangi birini geçersiz kılmanız gerekiyorsa

Genellikle Thread davranışını geçersiz kılmanıza gerek yoktur. Böylece Runnable uygular çoğu zaman tercih edilir.

Farklı bir notta, gelişmiş ExecutorServiceveyaThreadPoolExecutorService API daha fazla esneklik ve kontrol sağlar.

Bu SE Sorusuna bir göz atın:

Hizmetçi vs gündelik iplik spawner


5

Thread sınıfını Runnable uygulamasından ayırmak, thread ve run () yöntemi arasındaki olası senkronizasyon problemlerini de önler. Ayrı bir Runnable genellikle runnable koduna başvuru ve yürütme biçiminde daha fazla esneklik sağlar.


5

Budur S ait KATI Tek sorumluluğu:.

Bir iplik temsil çalışan içeriği arasında: (yığın çerçevesi, iplik kimliği, vs. çalışma bağlamında gibi) senkronize olmayan işlemler bir kod parçası arasında. Bu kod parçası ideal olarak, ister aynı uygulama olmalıdır senkron veya asenkron .

Bunları bir uygulamada bir araya getirirseniz, sonuçta ortaya çıkan nesneye ilişkisiz iki değişiklik nedeni verirsiniz :

  1. uygulamanızda iş parçacığı işleme (yani yürütme bağlamını sorgulama ve değiştirme)
  2. kod parçası tarafından çalıştırılan algoritma (çalıştırılabilir kısım)

Kullandığınız dil kısmi sınıfları veya çoklu kalıtımı destekliyorsa, her bir nedeni kendi süper sınıfında ayırabilirsiniz, ancak özellik kümeleri çakışmadığı için iki nesneyi oluşturmakla aynı şekilde kaynar. Bu teori için.

Pratikte, genel olarak, bir programın gereğinden fazla karmaşıklık taşımasına gerek yoktur. Belirli bir görev üzerinde çalışan bir iş parçacığınız varsa, bu görevi hiç değiştirmeden, muhtemelen görevleri ayrı sınıflar haline getirmenin bir anlamı yoktur ve kodunuz daha basit kalır.

Java bağlamında , tesis zaten orada olduğundan , doğrudan tek başına Runnablesınıflarla başlamak ve örneklerini Thread(veya Executor) örneklere geçirmek daha kolaydır . Bir kez kullanılan bu modele, bu kadar basit katedilebilen iplik durumda daha kullanıma (hatta okuma) değil daha zordur.


5

Temel sınıfı genişletmek yerine arabirim uygulamak istemenizin bir nedeni, zaten başka bir sınıfı genişletmenizdir. Yalnızca bir sınıfı genişletebilirsiniz, ancak istediğiniz sayıda arabirimi uygulayabilirsiniz.

İş parçacığını genişletirseniz, mantığınızın 'bu' dışında bir iş parçacığı tarafından yürütülmesini önlersiniz. Yalnızca bir iş parçacığının mantığınızı yürütmesini istiyorsanız, Runnable'ı uygulamak daha iyidir.


Evet, herhangi bir sınıfı genişleterek kendi mantığınızı uygulamakta özgür olmak için Runnable arabirimini uygulayarak, Runnable'ın en çok Thread sınıfına tercih edilmesinin nedeni budur.
Akash5288

5

runnable kullanıyorsanız, diğer sınıflarınızdan herhangi birine genişletmek için alandan tasarruf edebilirsiniz.


5

Sınıfımızın bir davranış biçiminde olmasını istediğimiz temel nedeni yeniden ziyaret edebilir miyiz Thread? Hiçbir sebep yok, sadece bir görevi yürütmek istedik, büyük olasılıkla asenkron modda, bu da görevin yürütülmesinin ana iş parçamızdan ve ana iş parçacığımızdan erken bitirilmesi durumunda beklemesi veya beklememesi gerektiği anlamına geliyor. dallı yol (görev) için.

Tüm amaç bu ise, özel bir iş parçacığının ihtiyacını nerede görebilirim. Bu, Sistemin İş Parçacığı Havuzundan bir RAW İş Parçacığı alıp görevimizi (sınıfımızın bir örneği olabilir) atayarak gerçekleştirilebilir ve işte budur.

Oops konseptine itaat edelim ve ihtiyacımız olan tipte bir sınıf yazalım. Bir şeyleri yapmanın, doğru şekilde yapmanın birçok yolu vardır.

Bir göreve ihtiyacımız var, bu yüzden bir Konu üzerinde çalıştırılabilecek bir görev tanımı yazın. Bu yüzden Runnable kullanın.

Her zaman hatırla implementsözel olarak bir davranış vermek için extendskullanılır ve bir özellik / özellik vermek için kullanılır.

İş parçacığının özelliğini istemiyoruz, bunun yerine sınıfımızın çalıştırılabilecek bir görev olarak davranmasını istiyoruz.


4

Evet, ThreadA çağrısını çağırırsanız, start yöntemini çağırmanız gerekmez ve run yöntemi yalnızca ThreadA sınıfını çağırdıktan sonra çağrıdır. Ancak ThreadB çağrısını kullanırsanız, çağrı çalıştırma yöntemi için başlangıç ​​iş parçacığının gerekli olması gerekir. Daha fazla yardımın varsa bana cevap ver.


4

Belirtilen tüm nedenlerle Runnable kullanmak en yararlı olduğunu düşünüyorum, ancak bazen kendi iplik durdurma yöntemimi oluşturabilir ve doğrudan oluşturduğum iş parçacığı olarak çağırabilirsiniz Konu genişletmek istiyorum.


4

Java birden fazla devralmayı desteklemediğinden, Thread sınıfını genişletirseniz, başka hiçbir sınıf genişletilmez.

Örneğin: Bir uygulama oluşturursanız, Applet sınıfını genişletmesi gerekir, böylece burada iş parçacığı oluşturmanın tek yolu Runnable arabirimini uygulamaktır.


4

Runnablebir arayüz, Threadbu arayüzü uygulayan bir sınıftır. Tasarım açısından bakıldığında, bir görevin nasıl tanımlandığı ve nasıl yürütüldüğü arasında temiz bir ayrım olmalıdır. Birincisi bir Runnalbeuygulamanın sorumluluğudur ve ikincisi Threadsınıfın işidir . Çoğu durumda uygulama Runnabletakip etmek için doğru yoldur.


4

İş parçacığı ve çalıştırılabilir arasındaki fark. İş parçacığı sınıfını kullanarak İş parçacığı oluşturuyorsak, o zaman oluşturduğumuz nesne sayısına eşit iş parçacığı sayısı. Runnable arabirimini uygulayarak iş parçacığı oluşturuyorsak, o zaman birden fazla iş parçacığı oluşturmak için tek bir nesne kullanabiliriz.

Bu nedenle, verilerimiz hassas değilse gereksinime bağlı olarak. Bu yüzden birden fazla iş parçacığı arasında paylaşılabilir Runnable arayüzü kullanılabilir.


4

İki sentimi buraya eklemek - Mümkün olduğunca her zaman kullanın implements Runnable. Aşağıda kullanmamalısınız neden iki uyarılar vardır extends Threads

  1. İdeal olarak hiçbir zaman Thread sınıfını genişletmemelisiniz; Threadsınıf yapılmalıdır final. En azından onun gibi yöntemler thread.getId(). Genişletme ile ilgili bir hata için bu tartışmaya bakın Thread.

  2. Bulmacaları çözmek isteyenler, Thread'ı genişletmenin başka bir yan etkisini görebilirler. Aşağıdaki kod, kimse bunu bildirmediğinde erişilemeyen kodu yazdırır.

Lütfen http://pastebin.com/BjKNNs2G adresine bakın .

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 ");
        }
    } 
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.