傳統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;
}
}