thread - runnable task java




“實現Runnable”與“擴展線程” (20)

  1. Java不支持多繼承,這意味著您只能擴展一個Java類,所以一旦擴展了Thread類,您就失去了機會,無法擴展或繼承Java中的另一個類。
  2. 在面向對象的編程中,擴展類通常意味著增加新的功能,修改或改進行為。 如果我們沒有對Thread進行任何修改,而是使用Runnable接口。
  3. Runnable接口表示一個Task ,可以通過純ThreadExecutors或任何其他方式執行。 因此,與Runnable相比, Task邏輯分離是很好的設計決策。
  4. 將任務分離為Runnable意味著我們可以重用任務,並且可以通過不同方式執行任務。 由於一旦完成,你無法重新啟動一個Thread ,再次RunnableThread的任務, Runnable是贏家。
  5. Java designer recognizes this and that's why Executors accept Runnable as Task and they have worker thread which executes those task.
  6. Inheriting all Thread methods are additional overhead just for representing a Task which can can be done easily with Runnable .

從我在Java中使用線程的時間開始,我發現了這兩種編寫線程的方法:

使用可運行的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

這兩個代碼塊有什麼重大區別?


tl; dr:實現Runnable更好。 但是,警告很重要

一般來說,我會推薦使用類似於Runnable東西而不是Thread因為它可以讓你的工作只與你的並發選擇鬆散結合。 例如,如果您使用Runnable並稍後決定這實際上不需要它自己的Thread ,則可以調用threadA.run()。

警告:在這裡,我強烈建議不要使用原始線程。 我更喜歡使用CallablesFutureTasks (從javadoc:“可取消的異步計算”)。 超時,適當的取消和現代並發支持的線程池的集成對於我來說比成堆的原始線程更有用。

後續:有一個FutureTask構造函數 ,允許您使用Runnables(如果這是您最熟悉的),並且仍然可以從現代並發工具中受益。 引用javadoc:

如果您不需要特定結果,請考慮使用表單的結構:

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

所以,如果我們用你的threadA替換它們的runnable ,我們得到以下結果:

new FutureTask<Object>(threadA, null)

另一個允許你更接近Runnables的選項是一個ThreadPoolExecutor 。 您可以使用execute方法來傳遞Runnable來執行“將來某個時候給定的任務”。

如果你想嘗試使用線程池,上面的代碼片段將變成如下所示(使用Executors.newCachedThreadPool()工廠方法):

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

Can we re-visit the basic reason we wanted our class to behave as a Thread ? There is no reason at all, we just wanted to execute a task, most likely in an asynchronous mode, which precisely means that the execution of the task must branch from our main thread and the main thread if finishes early, may or may not wait for the branched path(task).

If this is the whole purpose, then where do I see the need of a specialized Thread. This can be accomplished by picking up a RAW Thread from the System's Thread Pool and assigning it our task (may be an instance of our class) and that is it.

So let us obey the OOPs concept and write a class of the type we need. There are many ways to do things, doing it in the right way matters.

We need a task, so write a task definition which can be run on a Thread. So use Runnable.

Always remember implements is specially used to impart a behaviour and extends is used to impart a feature/property.

We do not want the thread's property, instead we want our class to behave as a task which can be run.


Difference between Extending Thread and Implementing Runnable are:


I find it is most useful to use Runnable for all the reasons mentioned, but sometimes I like to extend Thread so I can create my own thread stopping method and call it directly on the thread I have created.


If I am not wrong, it's more or less similar to

界面和抽像類有什麼區別?

extends establishes " Is A " relation & interface provides " Has a " capability.

Prefer implements Runnable :

  1. If you don't have to extend Thread class and modify Thread API default implementation
  2. If you are executing a fire and forget command
  3. If You are already extending another class

Prefer " extends Thread " :

  1. If you have to override any of these Thread methods as listed in oracle documentation page

Generally you don't need to override Thread behaviour. So implements Runnable is preferred for most of the times.

On a different note, using advanced ExecutorService or ThreadPoolExecutorService API provides more flexibility and control.

Have a look at this SE Question:

ExecutorService vs Casual Thread Spawner


One difference between implementing Runnable and extending Thread is that 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 class that implements Runnable is not a thread and just a class. For a Runnable to be executed by a Thread, you need to create an instance of Thread and pass the Runnable instance in as the target.

In most cases, the Runnable interface should be used if you are only planning to override the run() method and no other Thread methods. This is important because classes should not be subclassed unless the programmer intends on modifying or enhancing the fundamental behavior of the class.

當需要擴展超類時,實現Runnable接口比使用Thread類更合適。因為我們可以在實現Runnable接口的同時擴展另一個類來創建線程。但是,如果我們只擴展Thread類,我們不能從任何其他類繼承。


One reason you'd want to implement an interface rather than extend a base class is that you are already extending some other class. You can only extend one class, but you can implement any number of interfaces.

If you extend Thread, you're basically preventing your logic to be executed by any other thread than 'this'. If you only want some thread to execute your logic, it's better to just implement Runnable.


That's the S of SOLID : Single responsibility.

A thread embodies the running context (as in execution context: stack frame, thread id, etc.) of the asynchronous execution of a piece of code. That piece of code ideally should be the same implementation, whether synchronous or asynchronous .

If you bundle them together in one implementation, you give the resulting object two unrelated causes of change:

  1. thread handling in your application (ie. querying and modifying the execution context)
  2. algorithm implemented by the piece of code (the runnable part)

If the language you use supports partial classes or multiple inheritance, then you can segregate each cause in its own super class, but it boils down to the same as composing the two objects, since their feature sets don't overlap. That's for the theory.

In practice, generally speaking, a programme does not need to carry more complexity than necessary. If you have one thread working on a specific task, without ever changing that task, there is probably no point in making the tasks separate classes, and your code remains simpler.

In the context of Java , since the facility is already there , it is probably easier to start directly with stand alone Runnable classes, and pass their instances to Thread (or Executor ) instances. Once used to that pattern, it is not harder to use (or even read) than the simple runnable thread case.


This is discussed in Oracle's Defining and Starting a Thread tutorial:

Which of these idioms should you use? The first idiom, which employs a Runnable object, is more general, because the Runnable object can subclass a class other than Thread. The second idiom is easier to use in simple applications, but is limited by the fact that your task class must be a descendant of Thread. This lesson focuses on the first approach, which separates the Runnable task from the Thread object that executes the task. Not only is this approach more flexible, but it is applicable to the high-level thread management APIs covered later.

In other words, implementing Runnable will work in scenarios where your class extends a class other than Thread . Java does not support multiple inheritance. Also, extending Thread will not be possible when using some of the high-level thread management APIs. The only scenario where extending Thread is preferable is in a small application that won't be subject to updates in future. It is almost always better to implement Runnable as it is more flexible as your project grows. A design change won't have a major impact as you can implement many interfaces in java, but only extend one class.


if you use runnable you can save the space to extend to any of your other class.


因為:可運行

  • 為Runnable實現提供更多的靈活性來擴展另一個類
  • 將代碼與執行分離
  • 允許您從線程池,事件線程或將來以任何其他方式運行可運行的程序。

即使你現在不需要這些,你也可能在將來。 由於重寫Thread沒有任何好處,因此Runnable是更好的解決方案。


實例化一個接口給你的代碼和線程的實現提供了一個更清晰的分離,所以我更願意在這種情況下實現Runnable。


實際上,將RunnableThread相互比較並不明智。

這兩者在多線程中具有依賴性和關係,就像汽車的Wheel and Engine關係一樣。

我會說,多線程只有兩個步驟。 讓我指出我的觀點。

可運行:
當實現interface Runnable它意味著你正在創建一些run able在不同線程中run able東西。 現在創建可以在線程內運行的東西(可在線程內運行),並不意味著創建線程。
所以MyRunnableMyRunnable是一個帶有void run方法的普通類。 它的對象將是一些只有一個方法run普通對象,當調用時它將正常執行。 (除非我們在線程中傳遞對象)。

線:
class Thread ,我會說一個非常特殊的類,它具有啟動一個新線程的能力,該線程實際上可以通過start()方法啟用多線程。

為什麼不比較聰明?
因為我們需要這兩者來進行多線程。

對於多線程,我們需要兩件事情:

  • 可以在線程內運行的東西(Runnable)。
  • 可以啟動新線程(線程)的東西。

所以技術上和理論上他們都需要啟動一個線程,一個會運行 ,一個會運行 (像機動車輛的Wheel and Engine )。

這就是為什麼你不能用MyRunnable啟動一個線程,你需要將它傳遞給Thread一個實例。

但是可以僅使用class Thread來創建和運行class Thread因為Class Thread實現了Runnable所以我們都知道Thread也是一個Runnable裡面。

最後, ThreadRunnable對於多線程而言不是競爭對手或替代品的補充。


我不是專家,但我可以想到實現Runnable而不是擴展的一個原因。Thread:Java只支持單一繼承,所以你只能擴展一個類。

編輯:這最初說“實現一個接口需要更少的資源。” 同樣,但你需要創建一個新的Thread實例,所以這是錯誤的。


我會說有第三種方式:

public class Something {

    public void justAnotherMethod() { ... }

}

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

也許這受到我最近大量使用Javascript和Actionscript 3的影響,但這樣你的類不需要實現像Runnable這樣非常模糊的界面。


是的:實現Runnable是實現它的首選方法,IMO。 你並沒有真正專注於線程的行為。 你只是給它一些東西來運行。 這意味著composition哲學上 “更純粹”的方式。

實際上 ,這意味著您可以實現Runnable並從另一個類擴展。


有一件我感到驚訝的事情還沒有被提及,那就是實現Runnable可以讓你的類更加靈活。

如果你擴展線程,那麼你正在做的動作總是會在一個線程中。 但是,如果您實現Runnable ,則不必這樣做。 您可以在一個線程中運行它,或者將它傳遞給某種執行程序服務,或者將它作為單個線程應用程序中的任務傳遞(可能稍後運行,但在同一個線程中)。 如果你只是使用Runnable ,那麼這些選項比你將自己綁定到Thread更加開放。


這裡的每個人似乎都認為實現Runnable是一條路,我並不真的不同意它們,但在我看來,擴展Thread也是一個例子,事實上,你已經在代碼中展示了它。

如果實現Runnable,那麼實現Runnable的類無法控制線程名稱,它是可以設置線程名稱的調用代碼,如下所示:

new Thread(myRunnable,"WhateverNameiFeelLike");

但是如果你擴展線程,那麼你可以在類本身中進行管理(就像在你的例子中你將線程命名為ThreadB一樣)。 在這種情況下你:

A)可能會給它一個更有用的名稱用於調試目的

B)迫使那個名字被用於該類的所有實例(除非你忽略了它是一個線程,並且像上面這樣做,就好像它是一個Runnable一樣,但是我們在這裡討論的是約定,所以可以忽略我感覺到的可能性)。

例如,你甚至可以對其創建進行堆棧跟踪,並將其用作線程名稱。 這看起來可能很奇怪,但取決於代碼的結構,對於調試目的可能非常有用。

這可能看起來像一個小事情,但是如果你有一個非常複雜的應用程序,並且有很多線程,並且所有突然的事情都會“停止”(無論是出於死鎖的原因還是可能因為網絡協議中的缺陷明顯的 - 或其他無休止的原因),然後從所有線程被稱為“線程-1”,“線程-2”,“線程-3”的Java獲得堆棧轉儲並不總是非常有用(這取決於你的線程是如何結構化以及是否可以有用地告訴哪些是由他們的堆棧跟踪而來 - 如果您使用全部運行相同代碼的多個線程組,則並非總是可行)。

話雖如此,你當然也可以通過創建一個線程類的擴展來以通用的方式完成上述操作,該類將其名稱設置為其創建調用的堆棧跟踪,然後將其用於Runnable實現而不是標準的Java Thread類(見下文),但除了堆棧跟踪之外,還可能有更多的上下文特定信息,這些信息在調試的線程名稱中很有用(對可能處理的許多隊列或套接字之一的引用,例如,在這種情況下,您可能更喜歡擴展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"));
    }
}

這裡有一個比較這兩個名字的輸出示例:

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]

那麼多好的答案,我想在此添加更多。 這將有助於理解Extending v/s Implementing Thread
Extends綁定兩個類文件非常密切,並可能導致一些相當難處理代碼。

兩種方法都做同樣的工作,但有一些差異。
最常見的區別是

  1. 當你擴展Thread類時,在那之後你不能擴展你需要的任何其他類。 (如你所知,Java不允許繼承多個類)。
  2. 當您實現Runnable時,您可以為您的課程節省一個空間,以便將來或現在擴展任何其他課程。

然而,實現Runnable和擴展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.

以下示例可幫助您更清楚地理解

//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 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接口方法中,只有一個類的實例正在創建,並且已被不同的線程共享。 因此,每個線程訪問都會增加計數器的值。

而Thread類方法,你必須為每個線程訪問創建單獨的實例。 因此,為每個類實例分配不同的內存,並且每個具有單獨的計數器,值保持相同,這意味著不會發生增量,因為沒有任何對象引用是相同的。

何時使用Runnable?
當您想要從一組線程訪問相同的資源時,使用Runnable接口。 避免在這裡使用Thread類,因為創建多個對象會消耗更多的內存,並且會成為一個很大的性能開銷。

實現Runnable的類不是線程而只是一個類。 對於Runnable成為線程,您需要創建一個Thread實例並將其作為目標傳入。

在大多數情況下,如果您只打算覆蓋run()方法並且沒有其他Thread方法,則應該使用Runnable接口。 這很重要,因為除非程序員打算修改或增強班級的基本行為,否則班級不應被分類。

當需要擴展超類時,實現Runnable接口比使用Thread類更合適。 因為我們可以在實現Runnable接口的同時擴展另一個類來創建線程。

我希望這個能幫上忙!





java-threads