1. 程式人生 > >高階併發程式設計學習-callable與Future的使用

高階併發程式設計學習-callable與Future的使用

應用場景:FutureTask可用於非同步獲取執行結果或取消執行任務的場景。通過傳入Runnable或者Callable的任務給FutureTask,直接呼叫其run方法或者放入執行緒池執行,之後可以在外部通過FutureTaskget方法非同步獲取執行結果,因此,FutureTask非常適合用於耗時的計算,主執行緒可以在完成自己的任務後,再去獲取結果。另外,FutureTask還可以確保即使呼叫了多次run方法,它都只會執行一次Runnable或者Callable任務,或者通過cancel取消FutureTask的執行等。

注意:Callable介面不能替代Runnable,原因是Callable必須要和執行緒池Executors

結合使用。Future取得的結果型別和callable返回的結果型別必須一致,通過泛型來實現。Callable要採用ExceutorService的submit方法提交,返回的future物件可以取消任務。FutureTask是介面Future的唯一的實現類。

使用步驟:

        (1)  任務需要實現callable介面,注意這裡的泛型:

@Override
        public Integer call() throws Exception {
           Integer result=0;
            return result;
     }

(2)

生成任務並提交,存在兩種方式。

         第一種:

 
Future<String> future=threadPool.submit(new Callable<String>(){
			@Override
			public String call() throws Exception {
				Thread.sleep(2000);
				return "hello";
			}
});
使用例子:
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;


public class CallableAndFuture {
	public static void main(String[] args) {
		ExecutorService threadPool=Executors.newSingleThreadExecutor();
		//future的泛型型別必須要與callable的泛型型別相一致
		Future<String> future=threadPool.submit(new Callable<String>(){

			@Override
			public String call() throws Exception {
				Thread.sleep(2000);
				return "hello";
			}
			
		});
		System.out.println("等待結果....");
      try {
    	//future.get方法會一直等待,也會阻塞執行緒
		System.out.println("拿到結果:future.get方法的結果是"+future.get());
	} catch (InterruptedException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	} catch (ExecutionException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
	System.out.println("aaaaaaaaaaaa");
	threadPool.shutdown();//必須顯示關閉
	}

}

第二種:

FutureTask<Integer> ft = new FutureTask<Integer>(new ComputeTask(i, ""+i));
exec.submit(ft);
注意這裡必須為FutureTask而非Future,因為ExecutorService.submit()方法宣告為:

Future<?>submit(Runnabletask)或

Future<T> submit(Callable<T>task);

這就要求提交的任務需要實現了Runnable介面或Callable介面,因此這裡需要使用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.Future;
import java.util.concurrent.FutureTask;

public class FutureTaskForMultiCompute {
    
    public static void main(String[] args) {
        
        FutureTaskForMultiCompute inst=new FutureTaskForMultiCompute();
        // 建立任務集合
        List<Future<Integer>> taskList = new ArrayList<Future<Integer>>();
        // 建立執行緒池
        ExecutorService exec = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 10; i++) {
            //傳入Callable物件建立FutureTask注意這裡的ft實現了callable介面
            FutureTask<Integer> ft = new FutureTask<Integer>(new ComputeTask(i, ""+i));
            /*第二種提交方式為
        	Future<Integer>ft=exec.submit(new ComputeTask(i,""+i));*/
            taskList.add(ft);
            //提交給執行緒池執行任務,也可以通過exec.invokeAll(taskList)一次性提交所有任務;
            exec.submit(ft);
        }
        
        System.out.println("所有計算任務提交完畢, 主執行緒接著幹其他事情!");

        // 開始統計各計算執行緒計算結果
        Integer totalResult = 0;
        for (Future<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);

    }

    
}

import java.util.concurrent.Callable;

public 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秒鐘,觀察主執行緒行為,預期的結果是主執行緒會繼續執行,到要取得FutureTask的結果是等待直至完成。
            Thread.sleep(5000);
            System.out.println("子執行緒計算任務: "+taskName+" 執行完成!");
            return result;
        }
    }