[英]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
的工作是執行名為run
或call
的方法中的代碼。
正如其他正確答案所解釋的那樣, Runnable
接口的目的是它代表一個合同。 當您的代碼聲稱實現Runnable
接口時,您承諾您的代碼有一個名為run
的方法。
Java 編譯器注意到這個承諾並檢查合同是否已履行。 如果您傳遞的對象未能同時 (a) 聲明它實現Runnable
,並且(b) 攜帶不帶參數且不返回任何值的方法run
,則編譯器會將這種情況標記為錯誤。
因此,執行程序服務要求您將任務作為實現Runnable
(或Callable
)接口的類的對象提交,以保證當任務到達並在后台線程上執行時,該任務有一個名為run
(或call
Callable
)。
這是一些示例代碼。 請注意執行程序服務如何不關心您傳遞給它的submit
方法的對象類型。 您可以傳遞類Dog
、 SalesReport
或Payroll
的對象——都沒有關系。 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
如果您熟悉現代 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
由另外兩個接口擴展: RunnableFuture
和RunnableScheduledFuture
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.