- 1、Future 異步執行獲取執行結果
我們知道通過Thread+Runnable可異步執行某項操作,但是如何異步執行後,並獲取執行結果?
Thread和Runnable並沒有提供獲取執行結果的操作。
Runnable 是對任務的封裝。
在JDK1.5提供了Future和Callable 接口的定義,
Future是對異步執行線程並獲取結果的線程封裝,提供獲取執行結果或取消任務操作。
Callable<T> 接口是完成異步執行任務的封裝,T call()方法執行業務邏輯。
要想異步執行任務,並獲取結果,就必須同時實現Runnable和Future 2個接口,JDK提供了FutureTask<T> implements RunnableFuture<V> 類型。
獲取異步執行並獲取結果邏輯:
1、自定義業務實現 Callable<T> 接口,並實現call()
2、創建FutureTask<T> 實例,傳遞Callable實例
FutureTask<Integer> futureTask=new FutureTask<>(callable)
3、通過線程啓動任務
new Thead(futureTask).start()
4、如果想獲取結果,堵塞(內部是通過堵塞或輪詢實現)
T t=futureTask.get();
5、如果想取消任務,是通過信號標識取消的,需要在call()中實現,通過信號量取消邏輯
futureTask.cancel(true);
- 2、Future 異步執行,獲取執行結果案例
public class FutureTest {
//業務類(執行方法獲取返回值)
private static class BussInfo implements Callable<Integer> {
private Integer count;
public BussInfo(Integer i){
count=i;
}
//具體業務是對值加100
@Override
public Integer call() throws Exception {
//判斷任務是否取消
while(count<50000) {
if (Thread.currentThread().isInterrupted()) {
System.out.println("==================任務已取消================");
break;
} else {
count++;
}
}
return count;
}
}
public static void main(String[] args) {
//線程異步執行,獲取結果
BussInfo bussInfo=new BussInfo(10);
FutureTask<Integer> futureTask=new FutureTask<>(bussInfo);
new Thread(futureTask).start();
//模擬任務執行,有時可取消任務
Random rd=new Random();
int i = rd.nextInt(10);
try {
//線程會堵塞
Integer integer=0;
if(i>5) {
integer = futureTask.get();
}else{
//取消任務,使用的是信號,需要在call()寫信號判斷
futureTask.cancel(true);
}
System.out.println("integer = " + integer);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"-main is ended");
}
}
3、採用Future實現異步的缺點,採用ForkJoin 思想實現的ForkJoinPool完成任務拆分和合併案例
1、獲取結果需要堵塞get()或輪詢isDone(),未實現異步執行完後採用通知或回調的方法
2、控制多個Future 結果之間的依賴性,如部分完成後進行合併結果等操作
ForkJoin 案例,將大任務分成邏輯相同的小任務,各個任務之間沒有依賴。分而治之思想
* 1、ForkJoinPool 執行ForkJoin的線程池,提供多線程異步執行,提供線程Fork和Join操作
* 2、RecursiveAction 不需要返回結果的子任務,RecursiveTask<V> 需要返回結果
* 3、定義業務類繼承RecursiveAction或RecursiveTask<V>,在compute()方法中完成任務的Fork和Join操作
- 3.1、RecursiveAction 無返回值案例
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveAction;
import java.util.concurrent.TimeUnit;
/**
* @ClassName ForkJoinTest
* @Description ForkJoin 案例,將大任務分成邏輯相同的小任務,各個任務之間沒有依賴。分而治之思想
* 1、ForkJoinPool 執行ForkJoin的線程池,提供多線程異步執行,提供線程Fork和Join操作
* 2、RecursiveAction 不需要返回結果的子任務,RecursiveTask<V> 需要返回結果
* 3、定義業務類繼承RecursiveAction或RecursiveTask<V>,在compute()方法中完成任務的Fork和Join操作
* 案例:多任務把oldLst數組中的值複製到newLst中。
**/
public class ForkJoinActionTest {
private static int count=10000;
private static List<Integer> oldLst=new ArrayList<>(count);
private static List<Integer> newLst=new ArrayList<>(count);
//初始化就集合值
static {
for(int i=count;i>0;i--){
oldLst.add(i);
//數組需要提前初始化,否則修改不了值
newLst.add(0);
}
}
public static void main(String[] args) {
System.out.println("oldLst:"+oldLst.toString());
System.out.println("newLst:"+newLst.toString());
RecursiveActionTest action=new RecursiveActionTest(0,count,oldLst,newLst);
ForkJoinPool pool=ForkJoinPool.commonPool();
pool.submit(action);
try {
pool.awaitTermination(3000, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
pool.shutdown();
System.out.println("main is ended");
System.out.println("oldLst:"+oldLst.toString());
System.out.println("newLst:"+newLst.toString());
}
/*
* 不需要返回值的Fork案例;
* 多任務把一個數組中數據複製到另一個數組中*/
private static class RecursiveActionTest extends RecursiveAction{
//子任務閾值大小
private static Integer preSize=100;
private int start;
private int end;
private List<Integer> oldLst;
private List<Integer> newLst;
public RecursiveActionTest(int start,int end,List<Integer> oldLst,List<Integer> newLst){
this.start=start;
this.end=end;
this.oldLst=oldLst;
this.newLst=newLst;
}
@Override
protected void compute() {
if((end-start)<preSize){
System.out.println(Thread.currentThread().getName()+"-start:"+start+",end:"+end);
for(int i=start;i<end;i++){
newLst.set(i, oldLst.get(i));
}
//System.out.println(Thread.currentThread().getName()+"-------執行子任務----"+start+"-this:"+this.hashCode());
}else{
//分成子任務
int middle=(start+end)/2;
RecursiveActionTest action1=new RecursiveActionTest(start,middle,oldLst,newLst);
RecursiveActionTest action2=new RecursiveActionTest(middle,end,oldLst,newLst);
//執行所有子任務
RecursiveActionTest.invokeAll(action1,action2);
//System.out.println(Thread.currentThread().getName()+"========分配任務====="+middle+"-this:"+this.hashCode());
}
}
}
}
- 3.2、RecursiveTask<V> 有返回值案例
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;
/**
* @ClassName ForkJoinTaskTest
* @Description 採用多任務,對一個數組中的值進行求和運算
**/
public class ForkJoinTaskTest {
private static List<Integer> lst=new ArrayList<>();
static {
//初始化數組中的值
for(int i=0;i<10000;i++){
lst.add(i);
}
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
ForkJoinPool pool=ForkJoinPool.commonPool();//JDK1.8提供的共用池,默認線程數是CPU核數
ForkJoinTask<Integer> submit = pool.submit(new RecursiveTaskTest(0, lst.size(), lst));
//Fork&Join後獲取總結果,堵塞
Integer count = submit.get();
System.out.println("count = " + count);
}
//fork&join 操作,運算數組中所有數的和
private static class RecursiveTaskTest extends RecursiveTask<Integer>{
private int preSize=100;
private int start;
private int end;
private List<Integer> list;
public RecursiveTaskTest(int start,int end,List<Integer> list){
this.start=start;
this.end=end;
this.list=list;
}
@Override
protected Integer compute() {
Integer sum=0;
if(end-start<preSize){
for(int i=start;i<end;i++){
sum=sum+lst.get(i);
}
}else{
int middle=(end+start)/2;
RecursiveTaskTest action1=new RecursiveTaskTest(start,middle,list);
RecursiveTaskTest action2=new RecursiveTaskTest(middle,end,list);
//執行所有任務
RecursiveTaskTest.invokeAll(action1,action2);
//執行Join操作(堵塞)
sum=action1.join()+action2.join();
}
return sum;
}
}
}