futuretask用法及使用場景介紹
FutureTask可用于異步獲取執(zhí)行結(jié)果或取消執(zhí)行任務(wù)的場景。通過傳入Runnable或者Callable的任務(wù)給FutureTask,直接調(diào)用其run方法或者放入線程池執(zhí)行,之后可以在外部通過FutureTask的get方法異步獲取執(zhí)行結(jié)果,因此,F(xiàn)utureTask非常適合用于耗時(shí)的計(jì)算,主線程可以在完成自己的任務(wù)后,再去獲取結(jié)果。另外,F(xiàn)utureTask還可以確保即使調(diào)用了多次run方法,它都只會(huì)執(zhí)行一次Runnable或者Callable任務(wù),或者通過cancel取消FutureTask的執(zhí)行等。
1. FutureTask執(zhí)行多任務(wù)計(jì)算的使用場景
利用FutureTask和ExecutorService,可以用多線程的方式提交計(jì)算任務(wù),主線程繼續(xù)執(zhí)行其他任務(wù),當(dāng)主線程需要子線程的計(jì)算結(jié)果時(shí),在異步獲取子線程的執(zhí)行結(jié)果。
package futuretask;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
public class FutureTaskForMultiCompute {
public static void main(String[] args) {
FutureTaskForMultiCompute inst=new FutureTaskForMultiCompute();
// 創(chuàng)建任務(wù)集合
List<FutureTask<Integer>> taskList = new ArrayList<FutureTask<Integer>>();
// 創(chuàng)建線程池
ExecutorService exec = Executors.newFixedThreadPool(5);
for (int i = 0; i < 10; i++) {
// 傳入Callable對(duì)象創(chuàng)建FutureTask對(duì)象
FutureTask<Integer> ft = new FutureTask<Integer>(inst.new ComputeTask(i, ""+i));
taskList.add(ft);
// 提交給線程池執(zhí)行任務(wù),也可以通過exec.invokeAll(taskList)一次性提交所有任務(wù);
exec.submit(ft);
}
System.out.println("所有計(jì)算任務(wù)提交完畢, 主線程接著干其他事情!");
// 開始統(tǒng)計(jì)各計(jì)算線程計(jì)算結(jié)果
Integer totalResult = 0;
for (FutureTask<Integer> ft : taskList) {
try {
//FutureTask的get方法會(huì)自動(dòng)阻塞,直到獲取計(jì)算結(jié)果為止
totalResult = totalResult + ft.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
// 關(guān)閉線程池
exec.shutdown();
System.out.println("多任務(wù)計(jì)算后的總結(jié)果是:" + totalResult);
}
private class ComputeTask implements Callable<Integer> {
private Integer result = 0;
private String taskName = "";
public ComputeTask(Integer iniResult, String taskName){
result = iniResult;
this.taskName = taskName;
System.out.println("生成子線程計(jì)算任務(wù): "+taskName);
}
public String getTaskName(){
return this.taskName;
}
@Override
public Integer call() throws Exception {
// TODO Auto-generated method stub
for (int i = 0; i < 100; i++) {
result =+ i;
}
// 休眠5秒鐘,觀察主線程行為,預(yù)期的結(jié)果是主線程會(huì)繼續(xù)執(zhí)行,到要取得FutureTask的結(jié)果是等待直至完成。
Thread.sleep(5000);
System.out.println("子線程計(jì)算任務(wù): "+taskName+" 執(zhí)行完成!");
return result;
}
}
}
2. FutureTask在高并發(fā)環(huán)境下確保任務(wù)只執(zhí)行一次
在很多高并發(fā)的環(huán)境下,往往我們只需要某些任務(wù)只執(zhí)行一次。這種使用情景FutureTask的特性恰能勝任。舉一個(gè)例子,假設(shè)有一個(gè)帶key的連接池,當(dāng)key存在時(shí),即直接返回key對(duì)應(yīng)的對(duì)象;當(dāng)key不存在時(shí),則創(chuàng)建連接。對(duì)于這樣的應(yīng)用場景,通常采用的方法為使用一個(gè)Map對(duì)象來存儲(chǔ)key和連接池對(duì)應(yīng)的對(duì)應(yīng)關(guān)系,典型的代碼如下面所示:
private Map<String, Connection> connectionPool = new HashMap<String, Connection>();
private ReentrantLock lock = new ReentrantLock();
public Connection getConnection(String key){
try{
lock.lock();
if(connectionPool.containsKey(key)){
return connectionPool.get(key);
}
else{
//創(chuàng)建 Connection
Connection conn = createConnection();
connectionPool.put(key, conn);
return conn;
}
}
finally{
lock.unlock();
}
}
//創(chuàng)建Connection
private Connection createConnection(){
return null;
}
在上面的例子中,我們通過加鎖確保高并發(fā)環(huán)境下的線程安全,也確保了connection只創(chuàng)建一次,然而確犧牲了性能。改用ConcurrentHash的情況下,幾乎可以避免加鎖的操作,性能大大提高,但是在高并發(fā)的情況下有可能出現(xiàn)Connection被創(chuàng)建多次的現(xiàn)象。這時(shí)最需要解決的問題就是當(dāng)key不存在時(shí),創(chuàng)建Connection的動(dòng)作能放在connectionPool之后執(zhí)行,這正是FutureTask發(fā)揮作用的時(shí)機(jī),基于ConcurrentHashMap和FutureTask的改造代碼如下:
private ConcurrentHashMap<String,FutureTask<Connection>>connectionPool = new ConcurrentHashMap<String, FutureTask<Connection>>();
public Connection getConnection(String key) throws Exception{
FutureTask<Connection>connectionTask=connectionPool.get(key);
if(connectionTask!=null){
return connectionTask.get();
}
else{
Callable<Connection> callable = new Callable<Connection>(){
@Override
public Connection call() throws Exception {
// TODO Auto-generated method stub
return createConnection();
}
};
FutureTask<Connection>newTask = new FutureTask<Connection>(callable);
connectionTask = connectionPool.putIfAbsent(key, newTask);
if(connectionTask==null){
connectionTask = newTask;
connectionTask.run();
}
return connectionTask.get();
}
}
//創(chuàng)建Connection
private Connection createConnection(){
return null;
}
經(jīng)過這樣的改造,可以避免由于并發(fā)帶來的多次創(chuàng)建連接及鎖的出現(xiàn)。
總結(jié)
以上就是本文關(guān)于futuretask用法及使用場景介紹的全部內(nèi)容,希望對(duì)大家有所幫助。感興趣的朋友可以參閱:淺談Java多線程處理中Future的妙用(附源碼)、Java利用future及時(shí)獲取多線程運(yùn)行結(jié)果、Java多線程ForkJoinPool實(shí)例詳解等,有什么問題可以隨時(shí)留言,歡迎各位參閱本站其他相關(guān)專題。
相關(guān)文章
java中http請求之restTemplate配置超時(shí)時(shí)間問題解決
這篇文章主要介紹了java中http請求之restTemplate配置超時(shí)時(shí)間,本文給大家分享三種解決方法,結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-05-05
springboot2+es7使用RestHighLevelClient的示例代碼
本文主要介紹了springboot2+es7使用RestHighLevelClient的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07
SpringCloud gateway如何修改返回?cái)?shù)據(jù)
這篇文章主要介紹了SpringCloud gateway如何修改返回?cái)?shù)據(jù)的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06
Spring5使用JSR 330標(biāo)準(zhǔn)注解的方法
從Spring3.0之后,除了Spring自帶的注解,我們也可以使用JSR330的標(biāo)準(zhǔn)注解,本文主要介紹了Spring5使用JSR 330標(biāo)準(zhǔn)注解,感興趣的可以了解一下2021-09-09
RxJava中多種場景的實(shí)現(xiàn)總結(jié)
這篇文章給大家詳細(xì)介紹了RxJava中多種場景的實(shí)現(xiàn),對(duì)大家學(xué)習(xí)使用RxJava具有一定的參考借鑒價(jià)值,有需要的朋友們可以參考學(xué)習(xí),下面來一起看看吧。2016-10-10
淺談在Java中使用Callable、Future進(jìn)行并行編程
這篇文章主要介紹了淺談在Java中使用Callable、Future進(jìn)行并行編程,具有一定借鑒價(jià)值,需要的朋友可以參考下。2017-12-12
Java對(duì)象Serializable接口實(shí)現(xiàn)詳解
這篇文章主要介紹了Java對(duì)象Serializable接口實(shí)現(xiàn)詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-12-12

