优雅的使用线程池---ListeningExecutorService的使用
参考: google guava中文教程 https://wizardforcel.gitbooks.io/guava-tutorial/content/16.html
https://blog.csdn.net/u010900754/article/details/90742576
并发编程是一个难题,但是一个强大而简单的抽象可以显著的简化并发的编写。出于这样的考虑,Guava 定义了 ListenableFuture接口并继承了JDK concurrent包下的Future 接口,ListenableFuture 允许你注册回调方法(callbacks),在运算(多线程执行)完成的时候进行调用, 或者在运算(多线程执行)完成后立即执行。这样简单的改进,使得可以明显的支持更多的操作,这样的功能在JDK concurrent中的Future是不支持的。 在高并发并且需要大量Future对象的情况下,推荐尽量使用ListenableFuture来代替
使用异步编程接口获取返回值的方式有两种:
1.同步方式,也就是调用方主动获取,但是这时可能还没有返回结果,可能需要轮询;
2.回调方式,调用者在提交任务时,注册一个回调函数,任务执行完以后,自动触发回调函数通知调用者;这种实现方式需要在执行框架里植入一个扩展点,用于触发回调。
Java原生api里的Future属于第一种,Java8提供的CompletableFuture属于第二种;在Java8出来之前,guava也提供了基于回调的编程接口,也就是本次要说的ListenableFuture(其实看guava代码,里面有大量这玩意儿,不搞懂不行。。。)。
futureTask
简单demo:
public class FutureDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
Callable<Integer> call = new Callable<Integer>() {
@Override
public Integer call() throws Exception {
System.out.println("waiting~~~~~~");
Thread.sleep(3000);
return 10086;
}
};
FutureTask<Integer> futureTask = new FutureTask(call);
Thread thread = new Thread(futureTask);
thread.start();
//do something
System.out.println("other things");
Integer o = futureTask.get();
System.out.println(o);
}
}
Future
FutureTask实现Future接口,它定义了5个方法:
简单说明一下接口定义
boolean cancel(boolean mayInterruptInRunning); //取消一个任务,并返回取消结果。参数表示是否中断线程。
boolean isCancelled(); //判断任务是否被取消
Boolean isDone(); //判断当前任务是否执行完毕,包括正常执行完毕、执行异常或者任务取消。
V get() ;//获取任务执行结果,任务结束之前会阻塞。
V get(long timeout, TimeUnit unit); //在指定时间内尝试获取执行结果。若超时则抛出超时异常
FutureTask
- volatile int state:表示对象状态,volatile关键字保证了内存可见性。futureTask中定义了7种状态,代表了7种不同的执行状态
private static final int NEW = 0; //任务新建和执行中
private static final int COMPLETING = 1; //任务将要执行完毕
private static final int NORMAL = 2; //任务正常执行结束
private static final int EXCEPTIONAL = 3; //任务异常
private static final int CANCELLED = 4; //任务取消
private static final int INTERRUPTING = 5; //任务线程即将被中断
private static final int INTERRUPTED = 6; //任务线程已中断
Callable callable:被提交的任务
Object outcome:任务执行结果或者任务异常
volatile Thread runner:执行任务的线程
volatile WaitNode waiters:等待节点,关联等待线程
long stateOffset:state字段的内存偏移量
long runnerOffset:runner字段的内存偏移量
long waitersOffset:waiters字段的内存偏移量
ListenableFuture
public class ListenableFutureTest {
public static void main(String[] args) {
}
public static void testListenFuture() throws InterruptedException {
ListenableFuture<String> submit = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(1))
.submit(() -> {
Thread.sleep(2000L);
return "aync result";
});
Futures.addCallback(submit, new FutureCallback<String>() {
@Override
public void onSuccess(@Nullable String result) {
System.out.println("succeed, result: {}" + result);
}
@Override
public void onFailure(Throwable t) {
System.out.println("failed, t: {}" + t);
}
}, Executors.newSingleThreadExecutor());
Thread.sleep(100000);
}
}
MoreExecutors.listeningDecorator就是包装了一下ThreadPoolExecutor,目的是为了使用
private static class ListeningDecorator extends AbstractListeningExecutorService {
private final ExecutorService delegate;
ListeningDecorator(ExecutorService delegate) {
this.delegate = (ExecutorService)Preconditions.checkNotNull(delegate);
}
public final void execute(Runnable command) {
this.delegate.execute(command);
}
}
ListenableFuture 中的基础方法是addListener(Runnable, Executor), 该方法会在多线程运算完的时候,在Executor中执行指定的Runnable。
这里的delegate就是ThreadPoolExecutor,虽然还重写了execute,不过还是直接调用ThreadPoolExecutor里面的execute ListeningExecutorService pool=MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(5));
这样一个执行器就被new出来了,现在需要往里面放任务了
ListenableFuture future = pool.submit(task1);看看submit代码
public <T> ListenableFuture<T> submit(Callable<T> task) {
return (ListenableFuture)super.submit(task);
}
然后调用父类的submit
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask);
return ftask;
}
这里会调用重写的newTaskFor
protected final <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
return TrustedListenableFutureTask.create(callable);
}
class TrustedListenableFutureTask<V> extends TrustedFuture<V> implements RunnableFuture<V> {
private volatile InterruptibleTask<?> task;
static <V> TrustedListenableFutureTask<V> create(Callable<V> callable) {
return new TrustedListenableFutureTask(callable);
}
TrustedListenableFutureTask(Callable<V> callable) {
this.task = new TrustedListenableFutureTask.TrustedFutureInterruptibleTask(callable);
}
public void run() {
InterruptibleTask localTask = this.task;
if (localTask != null) {
localTask.run();
}
this.task = null;
}
}
创建了一个TrustedListenableFutureTask,里面有个task是TrustedFutureInterruptibleAsyncTask, 这里重写了Runnable的run方法,调用的就是这个task得run方法(也就是我们真正的任务)
private final class TrustedFutureInterruptibleAsyncTask extends InterruptibleTask<ListenableFuture<V>> {
private final AsyncCallable<V> callable;
TrustedFutureInterruptibleAsyncTask(AsyncCallable<V> callable) {
this.callable = (AsyncCallable)Preconditions.checkNotNull(callable);
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?