Future ,FutrueTask,ForkJoinPool 等異步多線程執行工具案例

  • 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;
        }
    }
}

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章