成人在线亚洲_国产日韩视频一区二区三区_久久久国产精品_99国内精品久久久久久久

您的位置:首頁(yè)技術(shù)文章
文章詳情頁(yè)

詳解Java Callable接口實(shí)現(xiàn)多線程的方式

瀏覽:33日期:2022-09-02 15:09:45

在Java 1.5以前,創(chuàng)建線程的2種方式,一種是直接繼承Thread,另外一種就是實(shí)現(xiàn)Runnable接口。無(wú)論我們以怎樣的形式實(shí)現(xiàn)多線程,都需要調(diào)用Thread類中的start方法去向操作系統(tǒng)請(qǐng)求io,cup等資源。因?yàn)榫€程run方法沒(méi)有返回值,如果需要獲取執(zhí)行結(jié)果,就必須通過(guò)共享變量或者使用線程通信的方式來(lái)達(dá)到效果,這樣使用起來(lái)就比較麻煩。

而自從Java 1.5開(kāi)始,就提供了Callable和Future,通過(guò)它們可以在任務(wù)執(zhí)行完畢之后得到任務(wù)執(zhí)行結(jié)果。

Callable和Future介紹

Callable接口代表一段可以調(diào)用并返回結(jié)果的代碼;Future接口表示異步任務(wù),是還沒(méi)有完成的任務(wù)給出的未來(lái)結(jié)果。所以說(shuō)Callable用于產(chǎn)生結(jié)果,F(xiàn)uture用于獲取結(jié)果。

Callable接口使用泛型去定義它的返回類型。Executors類提供了一些有用的方法在線程池中執(zhí)行Callable內(nèi)的任務(wù)。由于Callable任務(wù)是并行的(并行就是整體看上去是并行的,其實(shí)在某個(gè)時(shí)間點(diǎn)只有一個(gè)線程在執(zhí)行),我們必須等待它返回的結(jié)果。java.util.concurrent.Future對(duì)象為我們解決了這個(gè)問(wèn)題。在線程池提交Callable任務(wù)后返回了一個(gè)Future對(duì)象,使用它可以知道Callable任務(wù)的狀態(tài)和得到Callable返回的執(zhí)行結(jié)果。Future提供了get()方法讓我們可以等待Callable結(jié)束并獲取它的執(zhí)行結(jié)果。

Callable與Runnable

java.lang.Runnable吧,它是一個(gè)接口,在它里面只聲明了一個(gè)run()方法:

public interface Runnable { public abstract void run();}

由于run()方法返回值為void類型,所以在執(zhí)行完任務(wù)之后無(wú)法返回任何結(jié)果。Callable位于java.util.concurrent包下,它也是一個(gè)接口,在它里面也只聲明了一個(gè)方法,只不過(guò)這個(gè)方法叫做call():

public interface Callable<V> { /** * Computes a result, or throws an exception if unable to do so. * @return computed result * @throws Exception if unable to compute a result */ V call() throws Exception;}

可以看到,這是一個(gè)泛型接口,call()函數(shù)返回的類型就是傳遞進(jìn)來(lái)的V類型。

那么怎么使用Callable呢?

一般情況下是配合ExecutorService來(lái)使用的,在ExecutorService接口中聲明了若干個(gè)submit方法的重載版本:

<T> Future<T> submit(Callable<T> task);<T> Future<T> submit(Runnable task, T result);Future<?> submit(Runnable task);

Future

Future就是對(duì)于具體的Runnable或者Callable任務(wù)的執(zhí)行結(jié)果進(jìn)行取消、查詢是否完成、獲取結(jié)果。必要時(shí)可以通過(guò)get方法獲取執(zhí)行結(jié)果,該方法會(huì)阻塞直到任務(wù)返回結(jié)果。

Future類位于java.util.concurrent包下,它是一個(gè)接口

:<T> Future<T> submit(Callable<T> task);<T> Future<T> submit(Runnable task, T result);Future<?> submit(Runnable task);

在Future接口中聲明了5個(gè)方法,下面依次解釋每個(gè)方法的作用:

cancel方法 用來(lái)取消任務(wù),如果取消任務(wù)成功則返回true,如果取消任務(wù)失敗則返回false。參數(shù)mayInterruptIfRunning表示是否允許取消正在執(zhí)行卻沒(méi)有執(zhí)行完畢的任務(wù),如果設(shè)置true,則表示可以取消正在執(zhí)行過(guò)程中的任務(wù)。如果任務(wù)已經(jīng)完成,則無(wú)論mayInterruptIfRunning為true還是false,此方法肯定返回false,即如果取消已經(jīng)完成的任務(wù)會(huì)返回false;如果任務(wù)正在執(zhí)行,若mayInterruptIfRunning設(shè)置為true,則返回true,若mayInterruptIfRunning設(shè)置為false,則返回false;如果任務(wù)還沒(méi)有執(zhí)行,則無(wú)論mayInterruptIfRunning為true還是false,肯定返回true。

isCancelled方法 表示任務(wù)是否被取消成功,如果在任務(wù)正常完成前被取消成功,則返回 true。

isDone方法 表示任務(wù)是否已經(jīng)完成,若任務(wù)完成,則返回true;

get()方法 用來(lái)獲取執(zhí)行結(jié)果,這個(gè)方法會(huì)產(chǎn)生阻塞,會(huì)一直等到任務(wù)執(zhí)行完畢才返回;

get(long timeout, TimeUnit unit) 用來(lái)獲取執(zhí)行結(jié)果,如果在指定時(shí)間內(nèi),還沒(méi)獲取到結(jié)果,就直接返回null。

也就是說(shuō)Future提供了三種功能: 

1)判斷任務(wù)是否完成;

2)能夠中斷任務(wù);

3)能夠獲取任務(wù)執(zhí)行結(jié)果。

Future用于表示異步計(jì)算的結(jié)果。它的實(shí)現(xiàn)類有java.util.concurrent.FutureTask<V>和 javax.swing.SwingWorker<T,V>,Android平臺(tái)上如果不想分支線程阻塞主線程,又想取得分支線程的執(zhí)行結(jié)果,可以用FutureTask。

FutureTask

Java的類是單繼承的設(shè)計(jì),如果采用繼承Thread的方式實(shí)現(xiàn)多線程,則不能繼承其他的類,采用接口能夠更好的實(shí)現(xiàn)數(shù)據(jù)共享

FutureTask實(shí)現(xiàn)了RunnableFuture接口,這個(gè)接口的定義如下:

public interface RunnableFuture<V> extends Runnable, Future<V> { void run(); }

可以看到這個(gè)接口實(shí)現(xiàn)了Runnable和Future接口,接口中的具體實(shí)現(xiàn)由FutureTask來(lái)實(shí)現(xiàn)。這個(gè)類的兩個(gè)構(gòu)造方法如下 :

public FutureTask(Callable<V> callable) { if (callable == null) throw new NullPointerException(); sync = new Sync(callable); } public FutureTask(Runnable runnable, V result) { sync = new Sync(Executors.callable(runnable, result)); }

如上提供了兩個(gè)構(gòu)造函數(shù),一個(gè)以Callable為參數(shù),另外一個(gè)以Runnable為參數(shù)。這些類之間的關(guān)聯(lián)對(duì)于任務(wù)建模的辦法非常靈活,允許你基于FutureTask的Runnable特性(因?yàn)樗鼘?shí)現(xiàn)了Runnable接口),把任務(wù)寫成Callable,然后封裝進(jìn)一個(gè)由執(zhí)行者調(diào)度并在必要時(shí)可以取消的FutureTask。

FutureTask可以由執(zhí)行者調(diào)度,這一點(diǎn)很關(guān)鍵。它對(duì)外提供的方法基本上就是Future和Runnable接口的組合:get()、cancel、isDone()、isCancelled()和run(),而run()方法通常都是由執(zhí)行者調(diào)用,我們基本上不需要直接調(diào)用它。FutureTask類同時(shí)又實(shí)現(xiàn)了Runnable接口,所以可以直接提交給Thread、Executors執(zhí)行:

public class CallableAndFuture { public static void main(String[] args) { Callable<Integer> callable = new Callable<Integer>() { public Integer call() throws Exception { return new Random().nextInt(100); } }; FutureTask<Integer> future = new FutureTask<Integer>(callable); new Thread(future).start(); try { Thread.sleep(5000);// 可能做一些事情int result = future.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } }

public class CallableAndFuture { public static void main(String[] args) { //ExecutorService.submit() ExecutorService threadPool = Executors.newSingleThreadExecutor(); Future<Integer> future = threadPool.submit(new Callable<Integer>() { public Integer call() throws Exception { return new Random().nextInt(100); } }); try { Thread.sleep(5000);// 可能做一些事情int result = future.get()); //Future.get() } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } }

如果要執(zhí)行多個(gè)帶返回值的任務(wù),并取得多個(gè)返回值,可用CompletionService:

CompletionService相當(dāng)于Executor加上BlockingQueue,使用場(chǎng)景為當(dāng)子線程并發(fā)了一系列的任務(wù)以后,主線程需要實(shí)時(shí)地取回子線程任務(wù)的返回值并同時(shí)順序地處理這些返回值,誰(shuí)先返回就先處理誰(shuí)。

public class CallableAndFuture { public static void main(String[] args) { ExecutorService threadPool = Executors.newCachedThreadPool(); CompletionService<Integer> cs = new ExecutorCompletionService<Integer>(threadPool); for(int i = 1; i < 5; i++) { final int taskID = i; //CompletionService.submit() cs.submit(new Callable<Integer>() { public Integer call() throws Exception { return taskID; } }); } // 可能做一些事情 for(int i = 1; i < 5; i++) { try { int result = cs.take().get()); //CompletionService.take()返回Future } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } } }

或者不使用CompletionService:先創(chuàng)建一個(gè)裝Future類型的集合,用Executor提交的任務(wù)返回值添加到集合中,最后便利集合取出數(shù)據(jù)。

區(qū)別:

Future集合方法,submit的task不一定是按照加入自己維護(hù)的list順序完成的。從list中遍歷的每個(gè)Future對(duì)象并不一定處于完成狀態(tài),這時(shí)調(diào)用get()方法就會(huì)被阻塞住,如果系統(tǒng)是設(shè)計(jì)成每個(gè)線程完成后就能根據(jù)其結(jié)果繼續(xù)做后面的事,這樣對(duì)于處于list后面的但是先完成的線程就會(huì)增加了額外的等待時(shí)間。

而CompletionService的實(shí)現(xiàn)是維護(hù)一個(gè)保存Future對(duì)象的BlockingQueue。只有當(dāng)這個(gè)Future對(duì)象狀態(tài)是結(jié)束的時(shí)候,才會(huì)加入到這個(gè)Queue中,take()方法其實(shí)就是Producer-Consumer中的Consumer。它會(huì)從Queue中取出Future對(duì)象,如果Queue是空的,就會(huì)阻塞在那里,直到有完成的Future對(duì)象加入到Queue中。

所以,先完成的必定先被取出。這樣就減少了不必要的等待時(shí)間。

到此這篇關(guān)于詳解Java Callable接口實(shí)現(xiàn)多線程的方式的文章就介紹到這了,更多相關(guān)Java Callable接口多線程內(nèi)容請(qǐng)搜索好吧啦網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持好吧啦網(wǎng)!

標(biāo)簽: Java
相關(guān)文章: