Javaでの「実行可能な実装」と「スレッドの拡張」の比較


2119

私がJavaでスレッドを使ったときから、スレッドを作成する次の2つの方法を見つけました。

implements Runnable

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

それとも、とextends Thread

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

これら2つのコードブロックに大きな違いはありますか?


57
この質問をありがとう、答えは私が持っていた多くの誤解を解消しました。SOが存在する前にJavaスレッドを実行する正しい方法を調べましたが、そこには多くの誤った情報/古い情報がありました。
James McMahon、

5
スレッドを拡張したい理由が1つあります(ただし、お勧めしませんinterrupt()。先制的に処理できます。繰り返しますが、これはアイデアです。適切な場合に役立つ可能性がありますが、お勧めしません。
bestss

うまく説明された回答もご覧ください:stackoverflow.com/q/5562720/285594

@bestsss、私はあなたがinterrupt()を処理することについてあなたが何を意味するかもしれないかを困惑させようとしています。メソッドをオーバーライドしようとしていますか?
ボブ・クロス

8
はい。コードに従って、クラススレッドAは任意のクラスを拡張できますが、クラススレッドBは他のクラスを拡張できません
mani deepak

回答:


1673

はい:RunnableIMO、実装が推奨される方法です。スレッドの動作に特化しているわけではありません。実行する何かを与えるだけです。つまり、構成哲学的に「より純粋な」方法であるということです。

実用的な観点から、それはあなたが実装することができることを意味Runnableし、別のクラスから拡張にも。


151
正確に言えば、よく言います。それを拡張することにより、スレッドでどのような動作を上書きしようとしていますか?私はほとんどの人が動作を上書きしようとしているのではなく、スレッドの動作を使用しようとしていると主張します。
hooknc 2009

87
側のコメントとして、あなたはスレッドをインスタンス化した場合、あなたはJavaでメモリリーク<5(これはランナブルで発生しません)を作成しているそのstart()メソッドを呼び出していない:stackoverflow.com/questions/107823/...を
ナチョ・コロマ

25
Runnableの小さな利点の1つは、特定の状況でスレッドを使用したくない、または使用したくない場合に、コードを実行するだけの場合は、単にrun()を呼び出すオプションがあることです。例(非常に波状) if (numberCores > 4) myExecutor.excute(myRunnable); else myRunnable.run()
user949300 2013年

10
@ user949300を使用してそれを行うこともできextends Threadます。スレッディングが必要ない場合は、なぜ実装するのですかRunnable...
m0skit0

30
SierraとBatesを言い換えると、Runnableを実装する主な利点は、「ジョブ」を「ランナー」から構造的に分離していることです。
8bitjunkie 14

569

tl; dr:Runnableの実装が優れています。ただし、警告は重要です

一般的には、私のようなもの使用することをお勧めしますRunnableではなく、Threadそれはあなただけ緩く同時実行のあなたの選択と相まって、あなたの仕事を維持することを可能にするために。たとえば、aを使用しRunnable、後でこれが実際にそれ自体を必要としないことを決定した場合、Thread単にthreadA.run()を呼び出すことができます。

警告:この辺りで、生のスレッドを使用しないことを強くお勧めします。私はCallablesFutureTasks(javadocから:「キャンセル可能な非同期計算」)の使用を非常に好みます。タイムアウトの統合、適切なキャンセル、および最新の同時実行サポートのスレッドプーリングは、生のスレッドの山よりもはるかに便利です。

フォローアップ: Runnablesを使用できるFutureTaskコンストラクターがあり(それが最も快適な場合)、それでも最新の同時実行ツールの利点を利用できます。 javadocを引用するには

特定の結果が必要ない場合は、次の形式の構成の使用を検討してください。

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

したがって、それらrunnableをyour threadAに置き換えると、次のようになります。

new FutureTask<Object>(threadA, null)

Runnablesに近づくためのもう1つのオプションは、ThreadPoolExecutorです。executeメソッドを使用してRunnableを渡し、「将来の特定のタスク」を実行できます。

スレッドプールを使用したい場合、上記のコードフラグメントは次のようになります(Executors.newCachedThreadPool()ファクトリメソッドを使用)。

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

40
これは、受け入れられた回答のIMHOよりも優れています。1つは、あなたが持っているコードのスニペットはexecutorを閉じないため、私は何百万という人々がこれを誤解し、タスクをスポーンするたびに新しいExecutorを作成するという質問を目にします。es静的(または挿入)フィールドとしては1度しか作成されないため、より適切です。
artbristol 2012年

7
@artbristol、ありがとう!私は新しいエグゼキューターに同意しません(私たちはコードであなたが提案することをします)。元の回答を書く際に、元のフラグメントに類似した最小限のコードを書こうとしました。これらの回答の多くの読者が、それらをジャンプポイントとして使用することを期待する必要があります。私はjavadocの代わりを書くつもりはありません。私は効果的にそのためのマーケティング資料を書いています。この方法が好きなら、私たちが提供しなければならない他のすべての素晴らしいものを見るはずです...!
ボブ・クロス

3
私はこれについて少し遅いコメントをしていることを知っていますが、FutureTask直接対処することは一般的にあなたがやりたいことではありません。 ExecutorServicesは、/ に対して適切なものFutureを作成します。同様に、sとa /の場合も同様です。submitRunnableCallableScheduledExecutorServiceScheduledFuturescheduleRunnableCallable
Powerlord 2015

3
@Powerlord、私の意図は、OPにできるだけ一致するコードフラグメントを作成することでした。新しいFutureTaskは最適ではないことに同意しますが、説明の目的からは明らかです。
Bob Cross

257

この話の教訓:

一部の動作をオーバーライドする場合にのみ継承します。

または、次のように読み替えてください。

より少ない継承、より多くのインターフェイス。


1
同時実行オブジェクトの作成を開始する場合、これは常に問題になります!スレッドオブジェクト関数も必要ですか?
Liebertee、2015年

4
スレッドから継承する場合、ほとんどの場合、run()メソッドの動作をオーバーライドする必要があります。
ウォーレンデュー2016年

1
あなたはの動作をオーバーライドすることはできませんjava.lang.Threadオーバーライドすることでrun()方法を。その場合、start()私が推測するメソッドをオーバーライドする必要があります。通常java.lang.Threadrun()メソッドに実行ブロックを挿入することで、の動作を再利用します。
sura2k

継承は、一部の動作をオーバーライドするためだけでなく、一般的な動作を使用するためでもあります。そしてそれは逆であり、オーバーライドが多いほど、階層が悪くなります。
peja

232

良い答えがたくさんあるので、これについてさらに説明したいと思います。これは理解するのに役立ちますExtending v/s Implementing Thread
Extendsは2つのクラスファイルを非常に緊密にバインドし、一部のコードの処理をかなり困難にする可能性があります。

どちらのアプローチも同じ働きをしますが、いくつかの違いがあります。
最も一般的な違いは

  1. Threadクラスを拡張すると、その後は必要な他のクラスを拡張できなくなります。(ご存じのとおり、Javaでは複数のクラスを継承できません)。
  2. Runnableを実装すると、クラスのスペースを節約して、将来または現在、他のクラスを拡張できます。

ただし、Runnableの実装と拡張スレッドの1つの重要な違いは、
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.

次の例は、より明確に理解するのに役立ちます

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

上記のプログラムの出力。

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

Runnableインターフェースアプローチでは、クラスの1つのインスタンスのみが作成され、異なるスレッドによって共有されています。そのため、counterの値は、すべてのスレッドアクセスごとに増加します。

一方、スレッドクラスのアプローチでは、スレッドアクセスごとに個別のインスタンスを作成する必要があります。したがって、クラスインスタンスごとに異なるメモリが割り当てられ、それぞれに個別のカウンターがあります。値は同じままです。つまり、オブジェクト参照が同じでないため、インクリメントは発生しません。

Runnableを使用する場合
スレッドのグループから同じリソースにアクセスする場合は、Runnableインターフェースを使用します。複数のオブジェクトを作成するとメモリが多く消費され、パフォーマンスのオーバーヘッドが大きくなるため、ここではThreadクラスの使用を避けてください。

Runnableを実装するクラスは、スレッドではなく単なるクラスです。Runnableがスレッドになるためには、スレッドのインスタンスを作成し、それをターゲットとして渡す必要があります。

ほとんどの場合、run()メソッドをオーバーライドするだけで他のスレッドメソッドをオーバーライドしない場合は、Runnableインターフェイスを使用する必要があります。プログラマーがクラスの基本的な動作を変更または拡張するつもりでない限り、クラスをサブクラス化してはならないため、これは重要です。

スーパークラスを拡張する必要がある場合は、Threadクラスを使用するよりもRunnableインターフェイスを実装する方が適切です。スレッドを作成するためにRunnableインターフェースを実装しながら別のクラスを拡張できるからです。

これがお役に立てば幸いです。


40
あなたのコードは明らかに間違っています。つまり、それはそれがすることはしますが、あなたが示すつもりでしたことはしません。
zEro

38
明確にするために:実行可能なケースでは、同じImplementsRunnableインスタンスを使用して複数のスレッドを開始しましたが、スレッドのケースでは、異なるExtendsThreadインスタンスを作成して、明らかに示した動作につながります。メインメソッドの後半は、次のようになり 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(); ます。
zEro

19
私はまだあなたの意図を理解していませんが、ExtendsThreadの複数のインスタンスを作成した場合、それらはすべて1を返します(あなたが示したように)。Runnableで同じ結果を得るには、そこで同じことを行う、つまりImplementsRunnableの複数のインスタンスを作成します。
zEro

3
@zEroこんにちは、私は未来から来ました。コードのバージョンもThread増加している場合、ステートメント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は間違っていますか?そうでない場合、これを実証するケースは何ですか?
邪悪な洗濯機

4
ここに投稿されたコードは誤解を招くものであり、頭痛の種となる可能性があります。少し整理してくれた@zEroに感謝します。
Nikos 2016年

78

驚いたことに、まだ言及されていないことの1つは、実装Runnableすることでクラスがより柔軟になることです。

スレッドを拡張する場合、実行しているアクションは常にスレッド内になります。ただし、実装Runnableする必要はありません。スレッドで実行することも、何らかのエグゼキュータサービスに渡すことも、単一のスレッド化されたアプリケーション内でタスクとして渡すこともできます(後で実行される可能性がありますが、同じスレッド内で実行される場合があります)。にRunnableバインドするよりも、使用するだけの方がオプションはずっとオープンですThread


6
まあ、実際にはThreadオブジェクトでも同じことができます。なぜなら; Thread implements Runnable;-)しかし、これをで行う方RunnableThread
siegi

7
確かに、しかしThreadあなたが必要としない、そして多くの場合望まない多くの余分なものを追加します。自分が実際に行っていることと一致するインターフェースを実装する方が常によいでしょう。
Herms

74

他のクラスを実装または拡張するRunnable場合は、インターフェイスが最も適しています。それ以外の場合は、他のクラスが拡張または実装しないようにする場合は、Threadクラスが推奨されます。

最も一般的な違いは

ここに画像の説明を入力してください

いつextends Threadクラス、その後、あなたが必要な他のクラスを拡張することはできません。(ご存じのとおり、Javaでは複数のクラスを継承できません)。

するとimplements Runnable、クラスのスペースを節約して、将来または現在、他のクラスを拡張できます。

  • Javaは複数の継承をサポートしていません。つまり、Javaで拡張できるクラスは1つだけなので、一度Threadクラスを拡張すると、チャンスを失い、Javaで別のクラスを拡張または継承できなくなります。

  • オブジェクト指向プログラミングでは、クラスの拡張とは一般に、新しい機能を追加し、動作を変更または改善することを意味します。スレッドに変更を加えない場合は、代わりにRunnableインターフェイスを使用します。

  • Runnableインターフェースは、プレーンスレッド、エグゼキューター、またはその他の手段で実行できるタスクを表します。したがって、タスクをスレッドよりも実行可能として論理的に分離することは、適切な設計上の決定です。

  • タスクをRunnableとして分離することは、タスクを再利用できることを意味し、さまざまな手段からタスクを実行する自由があります。いったん完了すると、スレッドを再開することはできません。ここでも、Runnable vs Thread for task、Runnableが勝者です。

  • Javaデザイナはこれを認識しているため、エグゼキュータはRunnableをタスクとして受け入れ、それらのタスクを実行するワーカースレッドを持っています。

  • すべてのThreadメソッドの継承は、Runnableで簡単に実行できるタスクを表すための追加のオーバーヘッドです。

javarevisited.blogspot.comからの礼儀

これらは、JavaにおけるThreadとRunnableの顕著な違いの一部でした。スレッドとランナブルのその他の違いを知っている場合は、コメントで共有してください。私はこのシナリオで個人的にRunnable over Threadを使用しており、要件に基づいてRunnableまたはCallableインターフェイスを使用することをお勧めします。

ただし、大きな違いがあります。

いつextends Threadクラス、あなたのスレッドのそれぞれは、それに一意のオブジェクトと関連付けを作成します。するとimplements Runnable、同じオブジェクトを複数のスレッドで共有します。


73

実は、比較することは賢明ではありませんRunnableし、Threadお互いに。

この2つは、Wheel and Engine自動車の関係と同じように、マルチスレッドで依存関係を持っています。

2つのステップでマルチスレッド化する方法は1つだけです。私の主張をさせてください。


実行interface Runnable可能実装するとrun able、別のスレッドにあるものを作成することになります。スレッド内で実行できるもの(スレッド内で実行可能)を作成しても、スレッドを作成することにはなりません。
したがって、クラスMyRunnablevoid runメソッドを持つ通常のクラスにすぎません。そして、そのオブジェクトはrun、呼び出されたときに正常に実行されるメソッドのみを持ついくつかの通常のオブジェクトになります。(スレッドでオブジェクトを渡さない限り)。

スレッド:
class Thread、私は新しいスレッドを開始する機能を備えた非常に特殊なクラスであり、そのstart()メソッドを介してマルチスレッドを実際に有効にします。

比較するのが賢明ではありませんか?
マルチスレッドでは両方が必要だからです。

マルチスレッドでは、次の2つが必要です。

  • スレッド内で実行できるもの(Runnable)。
  • 新しいスレッド(スレッド)を開始できるもの。

だから、技術的、理論的にはそれらの両方のスレッドを開始する必要があり、一つはなり実行し、一つはなります、それが実行します(同様にWheel and Engine、自動車のを)。

これMyRunnableが、のインスタンスに渡す必要があるスレッドを開始できない理由ですThread

しかし、それだけで使用してスレッドを作成して実行することが可能となるclass Threadクラスので、Thread道具をRunnable、我々はすべて知っているようThreadでもありRunnable内側が。

最後にThreadRunnableマルチスレッディングのために互いに補完しあって、競合他社や置き換えではありません。


3
丁度!これは受け入れられる答えになるはずです。ところで私は質問が編集され、ThreadAもう意味をなさないと思います
idelvall

受け入れ答えはあなたの応答@idelvallのためのより多くのデリゲートのおかげで
サイフ

ベストアンサー!ありがとう!
MichaelYe

44

Runnableを実装する必要がありますが、Java 5以降で実行している場合は、それを起動せずにnew ThreadExecutorServiceを使用する必要があります。詳細については、Javaで単純なスレッドを実装する方法を参照してください。


5
単一のスレッドを起動したいだけの場合、ExecutorServiceはそれほど便利ではないと思います。
Powerlord、2009

1
私が学んだことから、一般的にスレッドを自分で開始するべきではありません。それをexecutorサービスに任せると、すべてがはるかに制御しやすくなります(スレッドの中断を待つなど)。また、単一のスレッドに関するものであることを示唆する質問には何も見当たりません。
Fabian Steeg、

4
シングルスレッドになることが事前にわかっている場合、マルチスレッドを使用する意味は何ですか。それでは、複数のスレッドがあり、この答えは価値があると仮定しましょう。
zEro 2013年

1
@zEroイベントディスパッチスレッドが1つしかない理由があると確信しています。個別のスレッドを持つことが最善であるが、複数のスレッドを持つことが最善ではない可能性があるのは、これが唯一のケースではないかと思います。
porcoesphino 2013

33

私はエキスパートではありませんが、スレッドを拡張する代わりにRunnableを実装する1つの理由を考えることができます。Javaは単一の継承のみをサポートするため、1つのクラスしか拡張できません。

編集:これは当初、「インターフェースの実装に必要なリソースが少なくて済む」と述べていました。同様に、ただし、いずれかの方法で新しいThreadインスタンスを作成する必要があるため、これは誤りでした。


実行可能では、ネットワーク呼び出しを行うことはできませんか?私がandroid.os.NetworkOnMainThreadExceptionを持っているので。しかし、スレッドを使用することで、ネットワーク呼び出しを行うことができます。私が間違っている場合は私を修正してください。
Nabeel Thobani、2014年

@NabeelThobani通常のJavaは気にしませんが、Androidのように聞こえます。とはいえ、私はAndroidに慣れていません。
Powerlord 2014年

1
@NabeelThobaniもちろんできます。おそらく、Runnableでスレッドを作成していません。
m0skit0

20

私は3番目の方法があると思います:

public class Something {

    public void justAnotherMethod() { ... }

}

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

たぶん、これは最近のJavaScriptとActionscript 3の多用に少し影響されていますが、この方法では、クラスはのようなかなり漠然としたインターフェースを実装する必要がありませんRunnable


38
これは実際には3番目の方法ではありません。あなたはまだRunnableを実装していますが、匿名で実行しています。
Don Roby、2011年

2
@Don Roby:違います。多くの場合、これは便利であり、含まれているクラス/メソッドのフィールドと最終的なローカル変数を使用できます。
Bart van Heukelom、2011年

6
@BartvanHeukelom便利ですが、違いはありません。これは、任意のタイプのネストされたクラス、つまり、内部クラス、ローカルクラス、ラムダ式で行うことができます。
xehpuk 2015年

18

Java 8のリリースに伴い、3番目のオプションがあります。

Runnable機能インターフェースです。つまり、そのインスタンスはラムダ式またはメソッド参照で作成できます。

あなたの例は次のように置き換えることができます:

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

またはExecutorService、メソッド参照を使用する場合:

executor.execute(runner::run)

これらは例よりもはるかに短いだけでなく、スレッドの動作に特化していないため、単一の責任やコンポジションの使用など、Runnableover を使用する他の回答で述べられている多くの利点Threadを備えています。この方法Runnableでは、例で行うように、必要なものがすべて追加のクラスを作成することも回避できます。


この答えは説明が必要です。いくつかの不可解な後、私はそれ() -> {}が誰かが必要とするカスタムロジックを表すことになっていると結論づけますか?だからそれは次のように言った方がいいでしょう() -> { /* Code here */ }か?
ToolmakerSteve

17

インターフェイスをインスタンス化すると、コードとスレッドの実装が明確に分離されるため、この場合はRunnableを実装することをお勧めします。


12

ここの誰もがRunnableを実装するのが最善の方法だと思っているようですが、私はそれらに同意しませんが、私の意見ではThreadを拡張することもあります。

Runnableを実装する場合、Runnableを実装するクラスはスレッド名を制御できません。次のように、スレッド名を設定できるのは呼び出しコードです。

new Thread(myRunnable,"WhateverNameiFeelLike");

ただし、Threadを拡張すると、クラス内でこれを管理できます(例のように、スレッドに「ThreadB」という名前を付けます)。この場合、あなたは:

A)デバッグのために、より便利な名前を付けることができます

B)その名前をそのクラスのすべてのインスタンスに使用することを強制しています(スレッドであるという事実を無視し、Runnableであるかのようにそれを使用して上記を実行する場合を除きますが、ここでは慣例について話し合っているため、私が感じるその可能性を無視してください)。

たとえば、その作成のスタックトレースを取得し、それをスレッド名として使用することもできます。これは奇妙に思えるかもしれませんが、コードの構造によっては、デバッグに非常に役立ちます。

これは小さなことのように思えるかもしれませんが、多くのスレッドを含む非常に複雑なアプリケーションがあり、突然すべてが「停止」しました(デッドロックの理由またはネットワークプロトコルの欠陥のため)明らか-またはその他の無限の理由)次に、すべてのスレッドが「Thread-1」、「Thread-2」、「Thread-3」と呼ばれるJavaからスタックダンプを取得することは、必ずしも非常に有用であるとは限りません(スレッドの状態によって異なります)構造化されており、スタックトレースによってどれがどれであるかを有益に判断できるかどうか-すべてが同じコードを実行している複数のスレッドのグループを使用している場合は、常に可能とは限りません)。

もちろん、名前をその作成呼び出しのスタックトレースに設定するスレッドクラスの拡張を作成し、それを標準のJavaスレッドクラスの代わりにRunnable実装で使用することにより、上記を一般的な方法で実行することもできます。 (下記を参照)しかし、スタックトレースに加えて、デバッグ用のスレッド名に役立つコンテキスト固有の情報(スレッドが処理できる多くのキューまたはソケットの1つへの参照など)がある場合があります。その場合のためにThreadを拡張して、コンパイラーに(またはライブラリーを使用している他の)特定の情報(たとえば、問題のキュー/ソケット)を渡して名前で使用させることができます。

以下は、呼び出しスタックトレースを名前として持つ一般的なスレッドの例です。

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

2つの名前を比較した出力のサンプルを次に示します。

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あなたのポイントは何ですか?スレッドの実行中に上記のコードを使用してスレッド作成のスタックトレースを取得することはできません(代わりに、単純な名前またはせいぜいスレッド起動のスタックトレースを取得します)スレッドをサブクラス化することにより、それを正確に実行して強制できますさらに、コンテキスト固有の情報を要求することで、どのスレッドに問題があるかを正確に理解することができます。
AntonyM

8
私の要点は、「Runnableを実装する場合、Runnableを実装するクラスはスレッド名を制御できない...」というのは明らかに誤りです。実装クラスは、Runnableコードを実行するスレッドは、定義により、現在のスレッド(およびあるように、実際に、スレッド名を制御することができるセキュリティチェックを通過する任意のコードスレッド名を制御しています)。投稿の半分を「omg、スレッド名はどうだ!」に費やしたことを考えると、それはちょっと大したことのようです。
cHao

スレッド名?スレッドクラスの拡張を止めるものは何もありません。
RichieHH 14

11

実行可能な理由:

  • Runnable実装がより柔軟に別のクラスを拡張できるようにする
  • コードを実行から分離する
  • スレッドプール、イベントスレッドから、または将来的には他の方法でランナブルを実行できます。

今はこれが必要ない場合でも、将来的には必要になる可能性があります。スレッドをオーバーライドしてもメリットがないため、Runnableの方が優れたソリューションです。


11

これは非常に人気のあるトピックであり、適切な回答はさまざまな場所に分散されて深く扱われているため、他のユーザーからの適切な回答をより簡潔な形式にまとめて、新規参入者が前もって簡単に概要を把握できるのは妥当だと感じました。

  1. 通常、クラスを拡張して機能を追加または変更します。だから、あなたがしたくない場合はする上書き任意のスレッドの動作を、その後のRunnableを使用しています。

  2. 同様に、スレッドメソッドを継承する必要がない場合は、Runnableを使用することで、そのオーバーヘッドなしで実行できます。

  3. 単一継承:スレッドを拡張する場合、他のクラスから拡張することはできないため、それが必要な場合は、Runnableを使用する必要があります。

  4. これは、その意味では、実行可能タスク持っている方が良い、技術的手段とは別のドメインロジックに良いデザインである分離あなたからタスクをあなたのランナーを

  5. 同じRunnable オブジェクトを複数回実行できますが、Threadオブジェクトは1回しか開始できません。(多分理由は、ExecutorsがRunnableを受け入れるが、スレッドを受け入れない理由です。)

  6. Runnableとしてタスクを開発する場合、現在および将来の使用方法にあらゆる柔軟性があります。エグゼキューターだけでなく、スレッドでも同時に実行できます。また、他の通常のタイプ/オブジェクトと同じように、同じスレッド内で非並行で使用/呼び出すこともできます。

  7. これにより、ユニットテストタスクロジックと同時実行の側面分離するのも簡単になります。

  8. この質問に興味がある場合は、CallableとRunnableの違いにも興味があるかもしれません。


@Pinoはい、スレッド自体もRunnableです。しかし、それを拡張してRunnableとして使用する場合、何が重要なのでしょうか。すべての荷物なしでプレーンなRunnableを使用しないのはなぜですか。したがって、Threadを拡張する場合は、1回しか使用できないstartメソッドを使用して実行することもできると私は主張します。それがニディッシュ・クリシュナンが彼の答えにしたかった点です。ここにあるのは、他の回答をまとめたものまたは簡単な要約にすぎないことに注意してください。
イェルク


8

これについては、Oracleのスレッド定義と開始のチュートリアルで説明されています。

これらのイディオムのどれを使用するべきですか?RunnableオブジェクトはThread以外のクラスをサブクラス化できるため、Runnableオブジェクトを使用する最初のイディオムはより一般的です。2番目のイディオムは単純なアプリケーションで使用する方が簡単ですが、タスククラスがThreadの子孫でなければならないという事実によって制限されます。このレッスンでは、Runnableタスクを、タスクを実行するThreadオブジェクトから分離する最初のアプローチに焦点を当てます。このアプローチはより柔軟であるだけでなく、後で説明する高レベルのスレッド管理APIにも適用できます。

つまり、実装Runnableは、クラスが以外のクラスを拡張するシナリオで機能しますThread。Javaは多重継承をサポートしていません。また、Thread一部の高レベルスレッド管理APIを使用している場合、拡張はできません。拡張Threadが望ましい唯一のシナリオは、将来の更新の対象とならない小さなアプリケーションです。Runnableプロジェクトの成長に応じてより柔軟になるため、ほとんどの場合、実装する方が優れています。多くのインターフェースをJavaで実装できるため、設計の変更による大きな影響はありませんが、1つのクラスしか拡張できません。


8

最も簡単な説明はRunnable、同じオブジェクトを複数のスレッドに割り当て、Threadが同じオブジェクトの状態と動作を共有です。

例えば、二つのスレッドがあると仮定スレッド1つのアレイとを入れ整数スレッド2はアレイが充填されている配列から整数値をとります。thread2が機能するためには、thread1が配列を満たしたかどうかにかかわらず、配列の状態を知る必要があることに注意してください。

実装Runnableすることで、オブジェクトを共有するための柔軟性が得られますが、extends Threadスレッドごとに新しいオブジェクトを作成できるため、thread1によって行われた更新はすべてthread2に失われます。


7

私が間違っていなければ、それは多かれ少なかれに似ています

インターフェイスと抽象クラスの違いは何ですか?

extendsは、「Is A」関係を確立し、インターフェースは「Has」機能を提供します。

優先実装にRunnableをします

  1. Threadクラスを拡張してThread APIのデフォルト実装を変更する必要がない場合
  2. fire and forgetコマンドを実行している場合
  3. すでに別のクラスを拡張している場合

スレッドの拡張」を優先

  1. Oracleのドキュメントページにリストされているこれらのスレッドメソッドのいずれかをオーバーライドする必要がある場合

通常、スレッドの動作をオーバーライドする必要はありません。したがって、Runnableを実装します、ほとんどの場合、が推奨されます。

別のノートでは、高度な使用ExecutorServiceまたはThreadPoolExecutorServiceAPIは、より多くの柔軟性と制御を提供します。

このSEの質問を見てください:

ExecutorServiceとカジュアルスレッドスポナー


5

ThreadクラスをRunnable実装から分離することで、スレッドとrun()メソッド間の潜在的な同期の問題も回避できます。別個のRunnableは、一般に、実行可能コードが参照および実行される方法に大きな柔軟性を与えます。


5

それがSOLIDSです:単一の責任。

スレッドが具現実行コンテキストの:(スタックフレーム、スレッドID、等の実行コンテキストのように)非同期実行コードの一部の。同期であれ非同期であれ、そのコードは理想的には同じ実装である必要があります

それらを1つの実装に一緒にバンドルすると、結果のオブジェクトに2つの無関係な変更の原因が与えられます。

  1. アプリケーションでのスレッド処理(つまり、実行コンテキストのクエリと変更)
  2. コードによって実行されるアルゴリズム(実行可能部分)

使用する言語が部分クラスまたは多重継承をサポートしている場合、それぞれの原因を独自のスーパークラスに分離できますが、2つのオブジェクトを構成するのと同じ結果になります。これは、機能セットが重複しないためです。それは理論のためです。

実際には、一般的に言えば、プログラムは必要以上に複雑にする必要はありません。特定のタスクを処理する1つのスレッドがあり、そのタスクを変更しない場合、タスクをクラスに分割しても意味がなく、コードはより単純なままです。

Javaのコンテキストでは、ファシリティはすでに存在しているため、スタンドアロンRunnableクラスから直接開始し、インスタンスをThread(またはExecutor)インスタンスに渡す方がおそらく簡単です。一度使用さそのパターンに、簡単な実行可能なスレッドの場合よりも使用(あるいは読み出し)しないように硬いです。


5

基本クラスを拡張するのではなく、インターフェースを実装したい理由の1つは、すでに他のクラスを拡張していることです。拡張できるクラスは1つだけですが、任意の数のインターフェースを実装できます。

スレッドを拡張する場合、基本的に、ロジックが「これ」以外のスレッドによって実行されるのを防ぎます。いくつかのスレッドだけでロジックを実行したい場合は、Runnableを実装することをお勧めします。


はい。Runnableインターフェースを実装することにより、任意のクラスを拡張して独自のロジックを自由に実装できます。そのため、RunnableがThreadクラスよりも主に推奨されています。
Akash5288 2013


5

私たちのクラスが Threadか?理由はまったくありません。おそらく非同期モードでタスクを実行したかっただけです。つまり、タスクの実行はメインスレッドから分岐する必要があり、メインスレッドが早く終了した場合、待機する場合と待機しない場合があります。分岐したパス(タスク)。

これが全体の目的である場合、専用スレッドの必要性はどこにあると思いますか。これは、システムのスレッドプールからRAWスレッドを取得し、それにタスク(クラスのインスタンスである場合があります)を割り当てることで実現できます。

それでは、OOPの概念に従い、必要なタイプのクラスを記述しましょう。物事を行うには多くの方法があり、正しい方法でそれを行うことが重要です。

タスクが必要なので、スレッドで実行できるタスク定義を記述します。したがって、Runnableを使用します。

常に覚えておくimplementsと、動作を伝えるために特別に使用されextends、機能/プロパティを伝えるために使用されます。

スレッドのプロパティは必要ありません。代わりに、クラスを実行可能なタスクとして動作させます。


4

はい、ThreadA callを呼び出す場合、startメソッドを呼び出す必要はなく、runメソッドはThreadAクラスのみを呼び出した後に呼び出されます。ただし、ThreadB呼び出しを使用する場合は、runメソッドの呼び出しに必要な開始スレッドが必要です。他にご不明な点がありましたら、返信してください。


4

上記のすべての理由でRunnableを使用するのが最も便利ですが、独自のスレッド停止メソッドを作成して、作成したスレッドで直接呼び出すことができるように、Threadを拡張したい場合があります。


4

Javaは多重継承をサポートしていないため、Threadクラスを拡張すると、他のクラスは拡張されません。

例:アプレットを作成する場合、アプレットを拡張する必要があるため、ここでスレッドを作成する唯一の方法はRunnableインターフェースを実装することです。


4

Runnableはインターフェースですが、Threadはこのインターフェースを実装するクラスです。設計の観点からは、タスクの定義方法と実行方法の間に明確な違いがあるはずです。前者はRunnalbe実装の責任であり、後者はThreadクラスの仕事です。ほとんどの場合、実装することRunnableが正しい方法です。


4

Threadとrunnableの違い。Threadクラスを使用してThreadを作成している場合、スレッドの数は作成したオブジェクトの数と同じです。実行可能なインターフェイスを実装してスレッドを作成する場合は、単一のオブジェクトを使用して複数のスレッドを作成できます。したがって、単一のオブジェクトが複数のスレッドで共有されるため、メモリの消費量が少なくなります。

したがって、データが機密ではないかどうかの要件によって異なります。したがって、Runnableインターフェイスを使用して、複数のスレッド間で共有できます。


4

ここに2セントを追加します- 可能な限り常に使用しますimplements Runnable。以下は、extends Threadsを使用してはならない理由に関する2つの警告です

  1. 理想的には、Threadクラスを拡張しないでください。Threadクラスがなされるべきですfinal。少なくともそのようなメソッドthread.getId()。sの拡張に関連するバグについては、この説明を参照してくださいThread

  2. パズルを解くのが好きな人は、スレッドを拡張することの別の副作用を見ることができます。以下のコードは、誰も通知していないときに到達できないコードを出力します。

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 ");
        }
    } 
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.