Java中Runnable和Callable分別什么時候使用
提到 Java 就不得不說多線程了,就算你不想說,面試官也得讓你說呀,對不對。那說到多線程,就不得提線程了(這不廢話嗎)。那說到線程,就不得不說Runnable和Callable這兩個家伙了。
說熟悉也是真熟悉,在剛學(xué)習(xí)多線程的時候,第一個例子大概就是下面這樣子的。
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("執(zhí)行線程" + Thread.currentThread().getName());
}
}).start();看到了 Runnable的身影,有時候還會看到Callable的。
但是說很熟悉吧,印象也不是很大,好像就用了一下這兩位的名號,然后剩下的部分就跟他倆沒啥關(guān)系了。
今天,我們就來看看這兩位到底是什么,有什么區(qū)別,什時候應(yīng)該用 Runnable,什么時候又應(yīng)該用 Callable。
Runnable
自從Java誕生,Runnable就存在了,元老中的元老了,在 1.5之前,如果你想使用線程,那必須要實(shí)現(xiàn)自 Runnable。因?yàn)榈搅?JDK1.5,JDK 才加入了Callable。
@FunctionalInterface
public interface Runnable {
public abstract void run();
}是不是接口非常簡單,就一個抽象方法。
其實(shí)還可以再簡化一下, @FunctionalInterface標(biāo)明這個接口是一個函數(shù)式接口。函數(shù)式接口是在 JDK8才加入的,為的就是實(shí)現(xiàn)函數(shù)式編程,就那種Lambada表達(dá)式。
所以在 JDK1.7中,是沒有@FunctionalInterface修飾的,簡簡單單。我們找到 JDK1.7的 Ruunable實(shí)現(xiàn),是下面這樣子
public interface Runnable {
public abstract void run();
}想了解更多函數(shù)式編程的話,可以看這篇文章:Lambda、函數(shù)式接口、Stream 一次性全給你
如果一個線程類要實(shí)現(xiàn) Runnable 接口,則這個類必須定義一個名為 run 的無參數(shù)方法。
實(shí)現(xiàn)了 Runnable 接口的類可以通過實(shí)例化一個 Thread 實(shí)例,并將自身作為目標(biāo)傳遞來運(yùn)行。
舉個例子
首先定義一個RunnableThread類,并實(shí)現(xiàn)自(implements)Runnable接口,然后重寫 run方法。
public class RunnableThread implements Runnable{
@Override
public void run() {
System.out.println("當(dāng)前線程名稱"+ Thread.currentThread().getName());
}
}使用RunnableThread作為線程類(Thread)實(shí)例化的參數(shù),然后調(diào)用run方法。
RunnableThread runnableThread = new RunnableThread(); Thread thread = new Thread(runnableThread); thread.start();
注意,是調(diào)用新 new 出來的 Thread 實(shí)例的start() 方法,不要調(diào)用run方法,雖然我們是重寫Runnable的 run方法的。調(diào)用 run方法并沒有創(chuàng)建線程的效果,而是直接在當(dāng)前線程執(zhí)行,就和執(zhí)行一個普通類的普通方法一模一樣。
為什么要調(diào)用 start()方法呢,我們看看 Thread的 start()方法實(shí)現(xiàn)中,其實(shí)是調(diào)用了一個名稱為 start0()的 native 方法,native 方法就不是用 Java 實(shí)現(xiàn)的了,而是在 JVM 層面的實(shí)現(xiàn)。
這個start0方法的主要邏輯就是啟動一個操作系統(tǒng)線程,并和 JVM 線程綁定,開辟一些空間來存儲線程狀態(tài)和上下文的數(shù)據(jù),然后執(zhí)行綁定的 JVM 線程(也就是我們實(shí)現(xiàn)了Runnable的類)的 run方法的代碼塊,從而執(zhí)行我們自定義的邏輯。
還可以用線程池的方式調(diào)用
ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
.setNameFormat("thread-pool-%d").build();
ExecutorService singleThreadPool = new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());
singleThreadPool.execute(runnableThread);
singleThreadPool.shutdown();如果 Runnable那么完美的話,就沒必要在 JDK1.5中加入和它超級相似的 Callable了。
Runnable有什么不完美的地方嗎?就是它的 run方法是沒有返回值的。
如果你想在主線程中拿到新開啟線程的返回值的話,Runnable就不太方便了。必須要借助共享變量來完成。
所以,如果你的場景是要有返回值的話, 就要 Callable出手了。
Callable
Callable是在 JDK1.5才加入的,為的就是彌補(bǔ) Runnable沒有返回值的缺陷,雖然絕大多數(shù)場景都可以用 Runnable來實(shí)現(xiàn)。
@FunctionalInterface
public interface Callable<V> {
V call() throws Exception;
}和 Runnable類似的,@FunctionalInterface也是后來加入的,可以不考慮,只是為了函數(shù)式寫法。
Callable接口只有一個 call方法,并且有一個泛型返回值,可以返回任何類型。
舉個例子
首先聲明一個類,實(shí)現(xiàn)自 Callable接口,返回值為字符串類型
public class CallableThread implements Callable<String> {
@Override
public String call() throws Exception {
return "線程名稱:" + Thread.currentThread().getName();
}
}在代碼中通過下面的方式調(diào)用
CallableThread callableThread = new CallableThread();
FutureTask<String> futureTask = new FutureTask<>(callableThread);
Thread thread = new Thread(futureTask);
thread.start();
String result = futureTask.get();
System.out.println("執(zhí)行結(jié)果= " + result);看上去就比 Runnable要復(fù)雜一點(diǎn),要借助FutureTask了,因?yàn)?Thread類沒有接受Callable的構(gòu)造函數(shù)。
使用 FutureTask.get()方法獲取執(zhí)行結(jié)果。
在日常的開發(fā)中,不建議直接這樣用,除非你明確的知道這樣做沒有問題,否則的話,推薦使用線程池的方式來使用。
CallableThread callableThread = new CallableThread();
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<String> future = executor.submit(callableThread);
String result = future.get();
System.out.println("任務(wù)執(zhí)行結(jié)果: " + result);
executor.shutdown();如何選擇用哪一個
取舍的基本原則就是需不需要返回值,如果不需要返回值,那直接就選 Runnable,不用猶豫。如果有返回值的話,那更不用猶豫,不要想著借助共享變量的方式。
另外還有一點(diǎn)就是是否需要拋出異常, Runnable是不接受拋出異常的,Callable可以拋出異常。
Runnable適合那種純異步的處理邏輯。比如每天定時計(jì)算報(bào)表,將報(bào)表存儲到數(shù)據(jù)庫或者其他地方,只是要計(jì)算,不需要馬上展示,展示內(nèi)容是在其他的方法中單獨(dú)獲取的。
比如那些非核心的功能,當(dāng)核心流程執(zhí)行完畢后,非核心功能就自己去執(zhí)行吧,至于成不成功的,不是特別重要。例如一個購物下單流程,下單、減庫存、加到用戶的訂單列表、扣款是核心功能,之后的發(fā)送APP通知、短信通知這些就啟動新線程去干去吧。
最后
Runnable 在 java.lang這個包下,而當(dāng)JDK1.5發(fā)布的時候,新加入的 Callable被安置在了 java.util.concurrent這個包下,這是 Java 里有名的并發(fā)編程相關(guān)包,各種鎖啊、多線程工具類啊,都被放在這個包下。按道理,Runnable 也應(yīng)該在這里才對。
可見再厲害的項(xiàng)目也是隨著項(xiàng)目的擴(kuò)大而慢慢的規(guī)劃,而前期的一些看似不太合理的地方,只能做兼容和妥協(xié)。
到此這篇關(guān)于Java中Runnable和Callable分別什么時候使用的文章就介紹到這了,更多相關(guān)Java Runnable Callable內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Java使用多線程批次查詢大量數(shù)據(jù)(Callable返回?cái)?shù)據(jù))方式
- Java通過Callable實(shí)現(xiàn)多線程
- Java多線程中Callable和Future的解讀
- Java中的Callable實(shí)現(xiàn)多線程詳解
- Java使用Callable接口實(shí)現(xiàn)多線程的實(shí)例代碼
- Java多線程實(shí)現(xiàn)之Callable詳解
- Java中Runnable與Callable接口的區(qū)別詳解
- 詳解Java中Callable和Future的區(qū)別
- Java使用Runnable和Callable實(shí)現(xiàn)多線程的區(qū)別詳解
- java面試常問的Runnable和Callable的區(qū)別
- Java并發(fā)教程之Callable和Future接口詳解
- Java中callable的實(shí)現(xiàn)原理
相關(guān)文章
SpringBoot中使用EasyExcel并行導(dǎo)出多個excel文件并壓縮zip后下載的代碼詳解
SpringBoot的同步導(dǎo)出方式中,服務(wù)器會阻塞直到Excel文件生成完畢,在處理大量數(shù)據(jù)的導(dǎo)出功能,本文給大家介紹了SpringBoot中使用EasyExcel并行導(dǎo)出多個excel文件并壓縮zip后下載,需要的朋友可以參考下2024-09-09
Eclipse+Webservice簡單開發(fā)實(shí)例
這篇文章主要介紹了Eclipse+Webservice簡單開發(fā)實(shí)例的相關(guān)資料,需要的朋友可以參考下2016-02-02
簡單了解Spring Web相關(guān)模塊運(yùn)行原理
這篇文章主要介紹了簡單了解Spring Web相關(guān)模塊運(yùn)行原理,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-06-06
Spring Boot靜態(tài)資源路徑的配置與修改詳解
最近在做SpringBoot項(xiàng)目的時候遇到了“白頁”問題,通過查資料對SpringBoot訪問靜態(tài)資源做了總結(jié),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-09-09
SpringBoot+MyBatisPlus+MySQL8實(shí)現(xiàn)樹形結(jié)構(gòu)查詢
這篇文章主要為大家詳細(xì)介紹了SpringBoot+MyBatisPlus+MySQL8實(shí)現(xiàn)樹形結(jié)構(gòu)查詢,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-06-06
Java實(shí)現(xiàn)手機(jī)號碼歸屬地查詢
這篇文章主要為大家詳細(xì)介紹了如何利用Java實(shí)現(xiàn)手機(jī)號碼歸屬地查詢功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-12-12
Jmeter如何獲取jtl文件中所有的請求報(bào)文詳解
JMeter的可以創(chuàng)建一個包含測試運(yùn)行結(jié)果的文本文件,這些通常稱為JTL文件,因?yàn)檫@是默認(rèn)擴(kuò)展名,但可以使用任何擴(kuò)展名,這篇文章主要給大家介紹了關(guān)于Jmeter如何獲取jtl文件中所有的請求報(bào)文的相關(guān)資料,需要的朋友可以參考下2021-09-09

