Guava之ListenableFuture使用

傳統JDK中的Future通過異步的方式計算返回結果:在多線程運算中可能或者可能在沒有結束返回結果,Future是運行中的多線程的一個引用句柄,確保在服務執行返回一個Result。

ListenableFuture可以允許你註冊回調方法(callbacks),在運算(多線程執行)完成的時候進行調用,  或者在運算(多線程執行)完成後立即執行。這樣簡單的改進,使得可以明顯的支持更多的操作,這樣的功能在JDK concurrent中的Future是不支持的。

ListenableFuture 中的基礎方法是addListener(Runnable, Executor), 該方法會在多線程運算完的時候,指定的Runnable參數傳入的對象會被指定的Executor執行。

添加回調(Callbacks)

多數用戶喜歡使用 Futures.addCallback(ListenableFuture<V>, FutureCallback<V>, Executor)的方式, 或者 另外一個版本version(譯者注:addCallback(ListenableFuture<V> future,FutureCallback<? super V> callback)),默認是採用 MoreExecutors.sameThreadExecutor()線程池, 爲了簡化使用,Callback採用輕量級的設計.  FutureCallback<V> 中實現了兩個方法:

  • onSuccess(V),在Future成功的時候執行,根據Future結果來判斷。
  • onFailure(Throwable), 在Future失敗的時候執行,根據Future結果來判斷。

ListenableFuture的創建

對應JDK中的 ExecutorService.submit(Callable) 提交多線程異步運算的方式,Guava 提供了ListeningExecutorService 接口, 該接口返回 ListenableFuture 而相應的 ExecutorService 返回普通的 Future。將 ExecutorService 轉爲 ListeningExecutorService,可以使用MoreExecutors.listeningDecorator(ExecutorService)進行裝飾。

 

package com.redisson;

import com.google.common.util.concurrent.*;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;

import javax.annotation.Nullable;
import java.util.concurrent.*;

/**
 * @Description TODO
 * @Date 2020/6/30 10:29
 * @Author zsj
 */
public class RedissonTest {
    public static void main(String[] args)  {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(4, 10, 60,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(200),
                new ThreadPoolExecutor.CallerRunsPolicy());
        ListeningExecutorService service = MoreExecutors.listeningDecorator(threadPoolExecutor);
        ListenableFuture future = service.submit(new Callable<String>() {
            @Override
            public String call(){
                Integer.parseInt("ww");
                return "22";
            }
        });
        Futures.addCallback(future, new FutureCallback<String>() {

            @Override
            public void onSuccess(String o) {
                System.out.println(o);
            }

            @Override
            public void onFailure(Throwable throwable) {
                throwable.printStackTrace();

            }
        });
        threadPoolExecutor.shutdown();
    }

}

3、CountDownLatch  結合ListenableFuture 

package com.redisson;

import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.*;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.*;

/**
 * @Description TODO
 * @Date 2020/7/2 9:23
 * @Author zsj
 */
public class Test {
    /**
     * 線程池
     */
    static ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(4, 10, 60,
            TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(200),
            new ThreadPoolExecutor.CallerRunsPolicy()
    );


    public static void main(String[] args) {

        List<String> result = Collections.synchronizedList(new ArrayList<>());
        List<String> list = Collections.synchronizedList(new ArrayList<>());

        //模擬原始數據
        for (int i = 0; i < 1211; i++) {
            list.add(i + "-");
        }
        int size = 50;//切分粒度,每size條數據,切分一塊,交由一條線程處理
        int countNum = 0;//當前處理到的位置
        int count = list.size() / size;//切分塊數
        int threadNum = 0;//使用線程數
        if (count * size != list.size()) {
            count++;
        }

        final CountDownLatch countDownLatch = new CountDownLatch(count);

        //使用Guava的ListeningExecutorService裝飾線程池
        ListeningExecutorService executorService = MoreExecutors.listeningDecorator(threadPoolExecutor);

        while (countNum < count * size) {
            //切割不同的數據塊,分段處理
            threadNum++;
            countNum += size;
            MyCallable myCallable = new MyCallable();
            myCallable.setList(ImmutableList.copyOf(
                    list.subList(countNum - size, list.size() > countNum ? countNum : list.size())));

            ListenableFuture listenableFuture = executorService.submit(myCallable);

            //回調函數
            int finalThreadNum = threadNum;
            Futures.addCallback(listenableFuture, new FutureCallback<List<String>>() {
                //任務處理成功時執行
                @Override
                public void onSuccess(List<String> list) {
                    countDownLatch.countDown();
                    System.out.println("第"+ finalThreadNum +"次處理完成");
                    result.addAll(list);
                }

                //任務處理失敗時執行
                @Override
                public void onFailure(Throwable throwable) {
                    countDownLatch.countDown();
                    System.out.println("處理失敗:" + throwable);
                }
            });

        }


        try {
            //設置時間,超時了直接向下執行,不再阻塞
//            countDownLatch.await(3, TimeUnit.SECONDS);
            //未設置超時時間 阻塞執行
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        result.forEach(s -> System.out.println(s));
        System.out.println("------------結果處理完畢,返回完畢,使用線程數量:" + threadNum);

    }

}

4、

package com.redisson;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Callable;

/**
 * @Description TODO
 * @Date 2020/7/2 9:21
 * @Author zsj
 */
public class MyCallable implements Callable {

    private List<String> list;

    @Override
    public List<String> call() throws Exception {
        List<String> listReturn = Collections.synchronizedList(new ArrayList<>());
        //模擬對數據處理,然後返回
        for (int i = 0; i < list.size(); i++) {
            listReturn.add(list.get(i) + ":處理時間:" + System.currentTimeMillis() + "---:處理線程:" + Thread.currentThread());
        }
        //模擬業務處理所需時間
        Thread.sleep(1000);
        return listReturn;
    }

    public void setList(List<String> list) {
        this.list = list;
    }
}

 

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