futuretask用法及使用場景介紹
FutureTask可用于異步獲取執(zhí)行結果或取消執(zhí)行任務的場景。通過傳入Runnable或者Callable的任務給FutureTask,直接調用其run方法或者放入線程池執(zhí)行,之后可以在外部通過FutureTask的get方法異步獲取執(zhí)行結果,因此,FutureTask非常適合用于耗時的計算,主線程可以在完成自己的任務后,再去獲取結果。另外,FutureTask還可以確保即使調用了多次run方法,它都只會執(zhí)行一次Runnable或者Callable任務,或者通過cancel取消FutureTask的執(zhí)行等。
1. FutureTask執(zhí)行多任務計算的使用場景
利用FutureTask和ExecutorService,可以用多線程的方式提交計算任務,主線程繼續(xù)執(zhí)行其他任務,當主線程需要子線程的計算結果時,在異步獲取子線程的執(zhí)行結果。
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)建任務集合
List<FutureTask<Integer>> taskList = new ArrayList<FutureTask<Integer>>();
// 創(chuàng)建線程池
ExecutorService exec = Executors.newFixedThreadPool(5);
for (int i = 0; i < 10; i++) {
// 傳入Callable對象創(chuàng)建FutureTask對象
FutureTask<Integer> ft = new FutureTask<Integer>(inst.new ComputeTask(i, ""+i));
taskList.add(ft);
// 提交給線程池執(zhí)行任務,也可以通過exec.invokeAll(taskList)一次性提交所有任務;
exec.submit(ft);
}
System.out.println("所有計算任務提交完畢, 主線程接著干其他事情!");
// 開始統計各計算線程計算結果
Integer totalResult = 0;
for (FutureTask<Integer> ft : taskList) {
try {
//FutureTask的get方法會自動阻塞,直到獲取計算結果為止
totalResult = totalResult + ft.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
// 關閉線程池
exec.shutdown();
System.out.println("多任務計算后的總結果是:" + 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("生成子線程計算任務: "+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秒鐘,觀察主線程行為,預期的結果是主線程會繼續(xù)執(zhí)行,到要取得FutureTask的結果是等待直至完成。
Thread.sleep(5000);
System.out.println("子線程計算任務: "+taskName+" 執(zhí)行完成!");
return result;
}
}
}
2. FutureTask在高并發(fā)環(huán)境下確保任務只執(zhí)行一次
在很多高并發(fā)的環(huán)境下,往往我們只需要某些任務只執(zhí)行一次。這種使用情景FutureTask的特性恰能勝任。舉一個例子,假設有一個帶key的連接池,當key存在時,即直接返回key對應的對象;當key不存在時,則創(chuàng)建連接。對于這樣的應用場景,通常采用的方法為使用一個Map對象來存儲key和連接池對應的對應關系,典型的代碼如下面所示:
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ā)的情況下有可能出現Connection被創(chuàng)建多次的現象。這時最需要解決的問題就是當key不存在時,創(chuàng)建Connection的動作能放在connectionPool之后執(zhí)行,這正是FutureTask發(fā)揮作用的時機,基于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;
}
經過這樣的改造,可以避免由于并發(fā)帶來的多次創(chuàng)建連接及鎖的出現。
總結
以上就是本文關于futuretask用法及使用場景介紹的全部內容,希望對大家有所幫助。感興趣的朋友可以參閱:淺談Java多線程處理中Future的妙用(附源碼)、Java利用future及時獲取多線程運行結果、Java多線程ForkJoinPool實例詳解等,有什么問題可以隨時留言,歡迎各位參閱本站其他相關專題。
相關文章
java中http請求之restTemplate配置超時時間問題解決
這篇文章主要介紹了java中http請求之restTemplate配置超時時間,本文給大家分享三種解決方法,結合實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-05-05
springboot2+es7使用RestHighLevelClient的示例代碼
本文主要介紹了springboot2+es7使用RestHighLevelClient的示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2022-07-07
淺談在Java中使用Callable、Future進行并行編程
這篇文章主要介紹了淺談在Java中使用Callable、Future進行并行編程,具有一定借鑒價值,需要的朋友可以參考下。2017-12-12

