簡體   English   中英

Java 線程:如何為線程實現可運行的工作

[英]Java Threading: How does implementing runnable work for threading

我知道如果你想要線程,你可以擴展線程或在 java 中實現可運行到多線程。 但是為什么非要實現一個接口讓java去線程呢? 使 Java 線程工作的可運行接口的重要性是什么? Java 的接口是否從某物擴展而來?

Runnable接口的唯一特殊之處在於它是Thread在其構造函數中采用的內容。 它只是一個普通的舊界面。

與大多數接口一樣,關鍵是您正在根據合同進行編程:您同意將要運行的代碼放在Runnable#run()實現中,並且Thread同意在另一個線程中運行該代碼(當您創建並用它啟動一個Thread )。

實際上“執行”多Thread處理的是線程(因為它與本機系統交互)。 Runnable的實現就是放置要告訴Thread運行的代碼的地方。

事實上,您可以實現一個Runnable並運行它,而無需讓它在單獨的線程中運行:

Runnable someCode = new Runnable() {
    public void run() {
       System.out.println("I'm a runnable");
    }
};
someCode.run();

所以Runnable本身與多線程沒有任何關系,它只是在對象中封裝一段代碼時擴展的標准接口。

在功能上,實現Runnable接口和擴展Thread類沒有區別。 但是在某些情況下,可能更喜歡實現Runnable接口。 考慮一下您的類必須從其他類繼承的情況,並且它還應該顯示線程功能。 由於您的類不能從多個類繼承(Java 不支持),因此在這種情況下您的類可以實現Runnable接口。

但是為什么非要實現一個接口讓java去線程呢?

您不需要,正如您之前所說,您可以擴展 Thread 對象並實現 public void run 方法。 如果你想要一種更有條理和靈活(是的,靈活)的方法,你肯定想要使用 Runnable,原因很明顯:代碼可重用性。

當我說有條理時,我想說的是維護一個

Runnable doSomething = new Runnable()
{
    @Override
    public void run()
    {
        goAndDoSomethingReallyHeavyWork();
    }
};

然后為另一個線程重用相同的可運行對象,或者在另一時刻重用同一個線程(是的,您實際上可以重用一個線程),而不是將 2 個或更多線程擴展到您將使用一次的對象中。

使 Java 線程工作的可運行接口的重要性是什么?

重要的是 Thread 對象將“知道”您的 Runnable 有一個方法 run 並在必須時執行它(因此停止、暫停和其他 Thread 操作)。

Java 的接口是否從某物擴展而來?

這個問題值得我 +1 給你。 我真的很想知道,但它似乎是語言的一個特性,而不是像擴展 Object 超類的每個其他對象一樣本身的產物。

我希望它有所幫助。 干杯

但是為什么非要實現一個接口讓java去線程呢?

當您創建一個線程擴展類 Thread 時,您不能再擴展任何其他類(多重繼承)。 另一方面,如果您使用 Runnable,如果需要,您可以獲得擴展任何類的繼承優勢。

除了上述之外,您還可以獲得內存和性能級別的好處。

ExecutorService.submit( Runnable task )

你說:

延長線程

我們不再需要直接訪問Thread類來並發運行代碼。 Java 5引入了 Executors 框架。 請參閱Oracle 的教程

執行程序服務管理在一個或多個后台線程上運行您的任務。 您可以從幾種類型的執行程序服務中進行選擇,這些服務通過Executors類實例化。

對於偶爾出現的一些短期任務,請使用由緩存線程池支持的執行程序服務。

ExecutorService executorService = Executors.newCachedThreadPool();
executorService.submit( yourRunnableObjectGoesHere ) ;

ExecutorService的工作是執行名為runcall的方法中的代碼。

正如其他正確答案所解釋的那樣, Runnable接口的目的是它代表一個合同。 當您的代碼聲稱實現Runnable接口時,您承諾您的代碼有一個名為run的方法。

Java 編譯器注意到這個承諾並檢查合同是否已履行。 如果您傳遞的對象未能同時 (a) 聲明它實現Runnable並且(b) 攜帶不帶參數且不返回任何值的方法run ,則編譯器會將這種情況標記為錯誤。

因此,執行程序服務要求您將任務作為實現Runnable (或Callable )接口的類的對象提交,以保證當任務到達並在后台線程上執行時,該任務有一個名為run (或call Callable )。

示例代碼

這是一些示例代碼。 請注意執行程序服務如何不關心您傳遞給它的submit方法的對象類型。 您可以傳遞類DogSalesReportPayroll的對象——都沒有關系。 executor 服務只關心傳遞給submit的對象有一個名為run的方法。

package work.basil.example;

import java.time.Duration;
import java.time.Instant;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Demo
{
    public static void main ( String[] args )
    {
        Demo app = new Demo();
        app.demo();
    }

    private void demo ( )
    {
        Runnable task = new Runnable()
        {
            @Override
            public void run ( )
            {
                System.out.println( "Doing this work on a background thread. " + Instant.now() );
            }
        };

        ExecutorService executorService = null;
        try
        {
            executorService = Executors.newCachedThreadPool();
            executorService.submit( task );
            executorService.submit( task );
            executorService.submit( task );

            // Wait a moment for the background threads to do their work.
            try
            {
                Thread.sleep( Duration.ofSeconds( 2 ).toMillis() );
            }
            catch ( InterruptedException e )
            {
                e.printStackTrace();
            }
        }
        finally
        {
            if ( Objects.nonNull( executorService ) ) { executorService.shutdown(); }
            System.out.println( "Ending the main thread. " + Instant.now() );
        }
    }
}

運行時:

Doing this work on a background thread. 2020-12-20T07:16:26.119414Z
Doing this work on a background thread. 2020-12-20T07:16:26.119176Z
Doing this work on a background thread. 2020-12-20T07:16:26.119255Z
Ending the main thread. 2020-12-20T07:16:28.124384Z

Lambda 語法

如果您熟悉現代 Java 中的 lambda 語法,我們可以將其縮短為定義Runnable實現的代碼行。 效果一樣,只是語法不同。

package work.basil.example;

import java.time.Duration;
import java.time.Instant;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Demo
{
    public static void main ( String[] args )
    {
        Demo app = new Demo();
        app.demo();
    }

    private void demo ( )
    {
        Runnable task = ( ) -> System.out.println( "Doing this work on a background thread. " + Instant.now() );

        ExecutorService executorService = null;
        try
        {
            executorService = Executors.newCachedThreadPool();
            executorService.submit( task );
            executorService.submit( task );
            executorService.submit( task );

            // Wait a moment for the background threads to do their work.
            try
            {
                Thread.sleep( Duration.ofSeconds( 2 ).toMillis() );
            }
            catch ( InterruptedException e )
            {
                e.printStackTrace();
            }
        }
        finally
        {
            if ( Objects.nonNull( executorService ) ) { executorService.shutdown(); }
            System.out.println( "Ending the main thread. " + Instant.now() );
        }
    }
}

遺產

您詢問:

Java 的接口是否從某物擴展而來?

Java 中的所有類都擴展了Object類或從Object擴展的其他類。

Java 中的接口不從任何類擴展。 請記住,接口只是一個契約,一些類可能選擇做出關於具有特定名稱的方法的承諾,這些方法接受特定類型的參數並返回特定類型的值。

Java 中的接口可以擴展一個或多個其他接口。 這樣做只會為聲稱實現該接口的類所做的承諾添加更多方法。 請注意, Runnable由另外兩個接口擴展: RunnableFutureRunnableScheduledFuture

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM