Callable在java中,創(chuàng)建線程一般有兩種方式,一種是繼承Thread類,一種是實(shí)現(xiàn)Runnable接口。然而,這兩種方式的缺點(diǎn)是在線程任務(wù)執(zhí)行結(jié)束后,無法獲取執(zhí)行結(jié)果。我們一般只能采用共享變量或共享存儲(chǔ)以及線程通信的方式實(shí)現(xiàn)獲得任務(wù)結(jié)果的目的; 不過,在java中,也提供了使用Callable和Future來實(shí)現(xiàn)獲取任務(wù)結(jié)果的操作。Callable用來執(zhí)行任務(wù),產(chǎn)生結(jié)果,而Future用來獲得結(jié)果; @FunctionalInterface
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;
}
Callable和Runnable的區(qū)別: 1.Callable能接受一個(gè)泛型,然后在call方法中返回一個(gè)這個(gè)類型的值,而Runnable的run方法沒有返回值; 2.Callable的call方法可以拋出異常,而Runnable的run方法不會(huì)拋出異常; Future模式Future莫斯的核心在于:去除了函數(shù)的等待時(shí)間,并使得原來需要等待的時(shí)間段可以用于處理其他業(yè)務(wù)邏輯; Future模式:對(duì)于多線程,如果線程A要等待線程B的結(jié)果,那么線程A沒有必要等待線程B,知道線程B有結(jié)果,可以先拿到一個(gè)未來的Future,等線程B有結(jié)果時(shí)再取真實(shí)的結(jié)果; package com.wn.callable;
import java.util.concurrent.*;
public class MyCallable implements Callable {
@Override
public Object call() throws Exception {
System.out.println("callable接口中重寫的call方法,可以有返回值并且拋出異常?。?!");
return "callable";
}
//方案一
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyCallable myCallable = new MyCallable();
//利用FutureTask執(zhí)行callable并且接受結(jié)構(gòu)
FutureTask<String> stringFutureTask = new FutureTask<String>(myCallable);
//利用線程執(zhí)行task任務(wù)
new Thread(stringFutureTask).start();
//接受結(jié)果,get方法會(huì)發(fā)生阻塞情況
System.out.println(stringFutureTask.get());
System.out.println("mycallable執(zhí)行完畢!");
}
}
package com.wn.callable;
import java.util.concurrent.*;
public class MyCallable implements Callable {
@Override
public Object call() throws Exception {
System.out.println("callable接口中重寫的call方法,可以有返回值并且拋出異常?。。?);
return "callable";
}
//方案二:submit(Callable task)
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyCallable myCallable = new MyCallable();
//創(chuàng)建一個(gè)線程
ExecutorService executorService = Executors.newFixedThreadPool(3);
//創(chuàng)建線程執(zhí)行任務(wù),接受任務(wù)結(jié)果
Future submit = executorService.submit(myCallable);
//接受返回值,get方法會(huì)阻塞當(dāng)前線程
System.out.println(submit.get());
System.out.println("利用線程池執(zhí)行mycallable,完畢!?。?);
//停止
executorService.shutdown();
}
}
Future常用方法V get():獲取異步執(zhí)行的結(jié)果,如果沒有結(jié)果可用,此方法會(huì)阻塞知道異步計(jì)算完成; V get(Long timeout,TimeUnit unit):獲取異步執(zhí)行結(jié)果,如果沒喲結(jié)果可用,此方法會(huì)阻塞,但是會(huì)有時(shí)間限制,如果阻塞時(shí)間超過設(shè)定的timeout時(shí)間,該方法將拋出異常; boolean isDone():如果任務(wù)執(zhí)行結(jié)束,無論是正常結(jié)束或是中途取消還是發(fā)生異常,都返回true; boolean isCanceller():如果任務(wù)完成前被取消,則返回true; boolean cancel(boolean mayInterrupRunning):如果任務(wù)還沒有開始,執(zhí)行cancel方法將返回false;如果任務(wù)已經(jīng)啟動(dòng),執(zhí)行cancel方法將以中斷執(zhí)行此任務(wù)線程的方式來試圖停止任務(wù),如果停止成功,返回true; 當(dāng)任務(wù)已經(jīng)啟動(dòng),執(zhí)行cancel(false)方法將不會(huì)對(duì)正在執(zhí)行的任務(wù)線程產(chǎn)生影響(讓線程正常執(zhí)行到完成),此時(shí)返回false; 當(dāng)任務(wù)已經(jīng)啟動(dòng),執(zhí)行cancel方法將返回false,MayInterruptRunning參數(shù)表示是否中斷執(zhí)行中的線程; 實(shí)際上Future提供了三種功能: 1.能夠中斷執(zhí)行中的任務(wù); 2.判斷任務(wù)是否執(zhí)行完成; 3.獲取任務(wù)執(zhí)行完成后的結(jié)果; get()方法的阻塞性package com.wn.callable;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.*;
public class FutureGet {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executor=Executors.newFixedThreadPool(2);
//創(chuàng)建一個(gè)Callable,三秒返回String類型
Callable callable = new Callable() {
@Override
public Object call() throws Exception {
Thread.sleep(3000);
System.out.println("callable方法執(zhí)行!");
return "callable";
}
};
System.out.println("提交任務(wù)之前:" getStringDate());
Future future = executor.submit(callable);
System.out.println("提供任務(wù)之后,獲取結(jié)果之前:" getStringDate());
System.out.println("獲取返回值:" future.get());
System.out.println("獲取到結(jié)果之后:" getStringDate());
}
public static String getStringDate(){
Date date = new Date();
SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss");
String dataString = format.format(date);
return dataString;
}
}
通過上面的輸出可以看到,在調(diào)動(dòng)submit提交任務(wù)之后,主線程本來是繼續(xù)運(yùn)行到future.get()的時(shí)候就阻塞了,一直等到任務(wù)執(zhí)行完畢,拿到了返回的返回值,主線程才會(huì)繼續(xù)運(yùn)行; 阻塞性是因?yàn)檎{(diào)用了get()方法時(shí),任務(wù)沒有執(zhí)行完畢,所以會(huì)一直等到任務(wù)完成,形成了阻塞; 任務(wù)是在調(diào)用submit方法時(shí)就開始執(zhí)行了,如果在調(diào)用get方法時(shí),任務(wù)已經(jīng)執(zhí)行完畢,那么就不會(huì)造成阻塞; 下面調(diào)用方法前先休眠四秒,這時(shí)能馬上得到返回值: System.out.println("提交任務(wù)之前:" getStringDate());
Future future = executor.submit(callable);
System.out.println("提供任務(wù)之后,獲取結(jié)果之前:" getStringDate());
Thread.sleep(4000);
System.out.println("休眠四秒:" getStringDate());
System.out.println("獲取返回值:" future.get());
System.out.println("獲取到結(jié)果之后:" getStringDate());
可以看到,因?yàn)樾菝吡怂拿?,任?wù)已經(jīng)執(zhí)行完畢,所以get方法立馬就得到了結(jié)果; submit(Runnable task)因?yàn)镽unnable是沒有返回值,所以如果submit一個(gè)Runnable的話,get得到的值肯定為null; public class SubmitRunnable {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executor= Executors.newFixedThreadPool(2);
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Future future = executor.submit(runnable);
System.out.println("獲得返回值:" future.get());
}
}
submit(Runnable task,T result)雖然傳入Runnable不能直接返回內(nèi)容,但是可以通過submit(Runnable task,T result)傳入一個(gè)載體,通過這個(gè)載體獲取返回值; 創(chuàng)建一個(gè)類Data: package com.wn.callable;
public class Data {
String name;
String sex;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
}
在Runnable的構(gòu)造方法中傳入: package com.wn.callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class MyThreadData implements Runnable {
Data data;
public MyThreadData(Data data) {
this.data = data;
}
@Override
public void run() {
try {
Thread.sleep(2000);
System.out.println("線程執(zhí)行:");
data.setName("張三");
data.setSex("女");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] arg) throws ExecutionException, InterruptedException {
ExecutorService executor= Executors.newFixedThreadPool(2);
Data data = new Data();
Future<Data> submit = executor.submit(new MyThreadData(data), data);
System.out.println("返回結(jié)果:" submit.get().getName() ",sex:" submit.get().getSex());
}
}
get(long var1,TimeUnit var3)前面用的get方法獲取返回值,因?yàn)榉椒ㄗ枞袝r(shí)需要等很久的時(shí)間,所以可以設(shè)置超時(shí)時(shí)間; package com.wn.callable;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.*;
public class GetTime {
public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
ExecutorService executor= Executors.newFixedThreadPool(2);
//創(chuàng)建一個(gè)Callable,三秒返回String類型
Callable callable = new Callable() {
@Override
public Object call() throws Exception {
Thread.sleep(3000);
System.out.println("callable方法執(zhí)行!");
return "callable";
}
};
System.out.println("提交任務(wù)之前:" getStringDate());
Future future = executor.submit(callable);
System.out.println("提供任務(wù)之后,獲取結(jié)果之前:" getStringDate());
System.out.println("獲取返回值:" future.get(2,TimeUnit.SECONDS));
System.out.println("獲取到結(jié)果之后:" getStringDate());
}
public static String getStringDate(){
Date date = new Date();
SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss");
String dataString = format.format(date);
return dataString;
}
}
? 如果在二秒內(nèi)沒有獲取,就拋出異常,主線程繼續(xù)運(yùn)行,不會(huì)在繼續(xù)阻塞; ? 來源:https://www./content-4-665401.html |
|
|