直接继承Thread或者实现Runnable接口都可以创建线程,但是这两种方法都没有返回值,也就是不能获取执行完的结果。因此java1.5就提供了Callable接口来实现这一场景,而Future和FutureTask就可以和Callable接口配合起来使用。
public interface Runnable {
/**
* When an object implementing interface Runnable
is used
* to create a thread, starting the thread causes the object's
* run
method to be called in that separately executing
* thread.
*
* The general contract of the method run
is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();
}
public interface Callable {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
new Thread(() -> {
log.debug("通过Runnable方式执行任务");
}).start();
new Thread(new Runnable() {
@Override
public void run() {
}
}).start();
FutureTask task = new FutureTask(new Callable() {
@Override
public Object call() throws Exception {
log.debug("通过Callable方式执行任务");
Thread.sleep(3000);
return "返回任务结果";
}
});
new Thread(task).start();
log.debug("结果:{}", task.get());
}
和 Callable 配合的有一个 Future 类,通过 Future 可以了解任务执行情况,或者取消任务的执行,还可获取任务执行的结果,这些功能都是 Runnable 做不到的,Callable 的功能要比 Runnable 强大。
Future就是对于具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果。 必要时可以通过get方法获取执行结果,该方法会阻塞直到任务返回结果。
Future实际采用FutureTask实现,该对象相当于是消费者和生产者的桥梁,消费者通过FutureTask 存储任务的处理结果,更新任务的状态:未开始、正在处理、已完成等。而生产者拿到的 FutureTask 被转型为 Future 接口,可以阻塞式获取任务的处理结果,非阻塞式获取任务处理状态。
FutureTask既可以被当做Runnable来执行,也可以被当做Future来获取Callable的返回结果。
把 Callable 实例当作 FutureTask 构造函数的参数,生成 FutureTask 的对象,然后把这个对象当作一个 Runnable 对象,放到线程池中或另起线程去执行,最后还可以通过FutureTask 获取任务执行的结果。
public static void main(String[] args) throws ExecutionException, InterruptedException {
Task task = new Task();
//构建futureTask
FutureTask futureTask = new FutureTask<>(task);
//作为Runnable入参
new Thread(futureTask).start();
System.out.println("task运行结果:"+futureTask.get());
}
static class Task implements Callable {
@Override
public Integer call() throws Exception {
System.out.println("子线程正在计算");
int sum = 0;
for (int i = 0; i < 100; i++) {
sum += i;
}
return sum;
}
}
通过get方法获取线程执行结果
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask ft1 = new FutureTask<>(new T1Task());
FutureTask ft2 = new FutureTask<>(new T2Task());
FutureTask ft3 = new FutureTask<>(new T3Task());
FutureTask ft4 = new FutureTask<>(new T4Task());
FutureTask ft5 = new FutureTask<>(new T5Task());
//构建线程池
ExecutorService executorService = Executors.newFixedThreadPool(5);
executorService.submit(ft1);
executorService.submit(ft2);
executorService.submit(ft3);
executorService.submit(ft4);
executorService.submit(ft5);
//获取执行结果
System.out.println(ft1.get());
System.out.println(ft2.get());
System.out.println(ft3.get());
System.out.println(ft4.get());
System.out.println(ft5.get());
executorService.shutdown();
}
static class T1Task implements Callable {
@Override
public String call() throws Exception {
System.out.println("T1:查询商品基本信息...");
TimeUnit.MILLISECONDS.sleep(5000);
return "商品基本信息查询成功";
}
}
static class T2Task implements Callable {
@Override
public String call() throws Exception {
System.out.println("T2:查询商品价格...");
TimeUnit.MILLISECONDS.sleep(50);
return "商品价格查询成功";
}
}
static class T3Task implements Callable {
@Override
public String call() throws Exception {
System.out.println("T3:查询商品库存...");
TimeUnit.MILLISECONDS.sleep(50);
return "商品库存查询成功";
}
}
static class T4Task implements Callable {
@Override
public String call() throws Exception {
System.out.println("T4:查询商品图片...");
TimeUnit.MILLISECONDS.sleep(50);
return "商品图片查询成功";
}
}
static class T5Task implements Callable {
@Override
public String call() throws Exception {
System.out.println("T5:查询商品销售状态...");
TimeUnit.MILLISECONDS.sleep(50);
return "商品销售状态查询成功";
}
}
使用线程池和Future,异步完成任务,相比同步的处理方式,时间上只需要等待其中耗时最长的一个任务执行完成即可,而同步的方式要累加所有的任务执行时间。注意,调用get方法会阻塞,后续的get调用要等待之前的get调用完毕。
Future 注意事项
当 for 循环批量获取 Future 的结果时容易 阻塞,get 方法调用时应使用 timeout限制Future 的生命周期不能后退。一旦完成了任务,它就永久停在了“已完成”的状态,不能从头再来 ,使用Callable 和Future 不会产生新的线程,线程的创建时钟是通过newThread()来完成的。
Future的局限性
从本质上说,Future表示一个异步计算的结果。它提供了isDone()来检测计算是否已经完成,并且在计算结束后,可以通过get()方法来获取计算结果。在异步计算中,Future确实是个非常优秀的接口。但是,它的本身也确实存在着许多限制:
Callable+Future 可以实现多个task并行执行,但是如果遇到前面的task执行较慢时需要阻塞等待前面的task执行完后面task才能取得结果。而CompletionService的主要功能就是一边生成任务,一边获取任务的返回值。让两件事分开执行,任务之间不会互相阻塞,可以实现先执行完的先取结果,不再依赖任务顺序了。
内部通过阻塞队列+FutureTask,实现了任务先完成可优先获取到,即结果按照完成先后顺序排序,内部有一个先进先出的阻塞队列,用于保存已经执行完成的Future,通过调用它的take方法或poll方法可以获取到一个已经执行完成的Future,进而通过调用Future接口实现类的get方法获取最终的结果。
CompletableFuture使用详解
简单的任务,用Future获取结果还好,但我们并行提交的多个异步任务,往往并不是独立的,很多时候业务逻辑处理存在串行[依赖]、并行、聚合的关系。如果要我们手动用 Fueture实现,是非常麻烦的。
CompletableFuture是Future接口的扩展和增强。CompletableFuture实现了Future接口,并在此基础上进行了丰富地扩展,完美地弥补了Future上述的种种问题。更为重要的是,CompletableFuture实现了对任务的编排能力。借助这项能力,我们可以轻松地组织不同任务的运行顺序、规则以及方式。从某种程度上说,这项能力是它的核心能力。而在以往,虽然通过CountDownLatch等工具类也可以实现任务的编排,但需要复杂的逻辑处理,不仅耗费精力且难以维护。
实现了CompletionStage接口: 执行某一个阶段,可向下执行后续阶段。异步执行,默认线程池是ForkJoinPool.commonPool() 。
CompletableFuture类自己也提供了anyOf()和allOf()用于支持多个CompletableFuture并行执行
public static CompletableFuture supplyAsync(Supplier supplier) {
return asyncSupplyStage(asyncPool, supplier);
}
/**
* Returns a new CompletableFuture that is asynchronously completed
* by a task running in the given executor with the value obtained
* by calling the given Supplier.
*
* @param supplier a function returning the value to be used
* to complete the returned CompletableFuture
* @param executor the executor to use for asynchronous execution
* @param the function's return type
* @return the new CompletableFuture
*/
public static CompletableFuture supplyAsync(Supplier supplier,
Executor executor) {
return asyncSupplyStage(screenExecutor(executor), supplier);
}
/**
* Returns a new CompletableFuture that is asynchronously completed
* by a task running in the {@link ForkJoinPool#commonPool()} after
* it runs the given action.
*
* @param runnable the action to run before completing the
* returned CompletableFuture
* @return the new CompletableFuture
*/
public static CompletableFuture runAsync(Runnable runnable) {
return asyncRunStage(asyncPool, runnable);
}
/**
* Returns a new CompletableFuture that is asynchronously completed
* by a task running in the given executor after it runs the given
* action.
*
* @param runnable the action to run before completing the
* returned CompletableFuture
* @param executor the executor to use for asynchronous execution
* @return the new CompletableFuture
*/
public static CompletableFuture runAsync(Runnable runnable,
Executor executor) {
return asyncRunStage(screenExecutor(executor), runnable);
}
join()和get()方法都是用来获取CompletableFuture异步之后的返回值。join()方法抛出的是uncheck异常(即未经检查的异常),不会强制开发者抛出。get()方法抛出的是经过检查的异常,ExecutionException, InterruptedException 需要用户手动处理(抛出或者 try catch)
public CompletableFuture whenComplete(
BiConsumer super T, ? super Throwable> action) {
return uniWhenCompleteStage(null, action);
}
public CompletableFuture whenCompleteAsync(
BiConsumer super T, ? super Throwable> action) {
return uniWhenCompleteStage(asyncPool, action);
}
public CompletableFuture whenCompleteAsync(
BiConsumer super T, ? super Throwable> action, Executor executor) {
return uniWhenCompleteStage(screenExecutor(executor), action);
}
public CompletableFuture exceptionally(
Function fn) {
return uniExceptionallyStage(fn);
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture future = CompletableFuture.supplyAsync(() -> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
}
if (new Random().nextInt(10) % 2 == 0) {
int i = 12 / 0;
}
System.out.println("执行结束!");
return "test";
});
future.whenComplete(new BiConsumer() {
@Override
public void accept(String t, Throwable action) {
System.out.println(t+" 执行完成!");
}
});
future.exceptionally(new Function() {
@Override
public String apply(Throwable t) {
System.out.println("执行失败:" + t.getMessage());
return "异常xxxx";
}
}).join();
}
所谓结果转换,就是将上一段任务的执行结果作为下一阶段任务的入参参与重新计算,产生新的结果。
接收一个函数作为参数,使用该函数处理上一个CompletableFuture 调用的结果,并返回一个具有处理结果的Future对象。
thenApply 和 thenCompose的区别
与结果处理和结果转换系列函数返回一个新的 CompletableFuture 不同,结果消费系列函数只对结果执行Action,而不返回新的计算值。
根据对结果的处理方式,结果消费函数又分为:
通过观察该系列函数的参数类型可知,它们是函数式接口Consumer,这个接口只有输入,没有返回值
thenAcceptBoth 函数的作用是,当两个 CompletionStage 都正常完成计算的时候,就会执行提供的action消费两个异步的结果。
thenRun 也是对线程任务结果的一种消费函数,与thenAccept不同的是,thenRun 会在上一阶段CompletableFuture计算完成的时候执行一个Runnable,Runnable并不使用该CompletableFuture 计算的结果。
thenCombine 方法,合并两个线程任务的结果,并进一步处理。
所谓线程交互,是指将两个线程任务获取结果的速度相比较,按一定的规则进行下一步处理
两个线程任务相比较,先获得执行结果的,就对该结果进行下一步的转化操作。
两个线程任务相比较,先获得执行结果的,就对该结果进行下一步的消费操作。
两个线程任务相比较,有任何一个执行完成,就进行下一步操作,不关心运行结果。
两个线程任务相比较,两个全部执行完成,才进行下一步操作,不关心运行结果。
anyOf 方法的参数是多个给定的 CompletableFuture,当其中的任何一个完成时,方法返回这个 CompletableFuture。
allOf方法用来实现多 CompletableFuture 的同时返回。
sruptor简介
Disruptor是英国外汇交易公司LMAX开发的一个高性能队列,研发的初衷是解决内存队列的延迟问题(在性能测试中发现竟然与I/O操作处于同样的数量级)。基于Disruptor开发的系统单线程能支撑每秒600万订单,2010年在QCon演讲后,获得了业界关注。2011年,企业应用软件专家Martin Fowler专门撰写长文介绍。同年它还获得了Oracle官方的Duke大奖。目前,包括Apache Storm、Camel、Log4j 2在内的很多知名项目都应用了Disruptor以获取高性能。注意,这里所说的队列是系统内部的内存队列,而不是Kafka这样的分布式队列。Disruptor实现了队列的功能并且是一个有界队列,可以用于生产者-消费者模型。
Disruptor通过以下设计来解决队列速度慢的问题:
使用RingBuffer来作为队列的数据结构,RingBuffer就是一个可自定义大小的环形数组。除数组外还有一个序列号(sequence),用以指向下一个可用的元素,供生产者与消费者使用。
Disruptor要求设置数组长度为2的n次幂。在知道索引(index)下标的情况下,存与取数组上的元素时间复杂度只有O(1),而这个index我们可以通过序列号与数组的长度取模来计算得出,index=sequence % entries.length。也可以用位运算来计算效率更高,此时array.length必须是2的幂次方,index=sequece&(entries.length-1)
当所有位置都放满了,再放下一个时,就会把0号位置覆盖掉 。
单个生产者
1. 申请写入m个元素;
2. 若是有m个元素可以写入,则返回最大的序列号。这里主要判断是否会覆盖未读的元素;
3. 若是返回的正确,则生产者开始写入元素;
多个生产者
1. 申请写入m个元素;
2. 若是有m个元素可以写入,则返回最大的序列号。每个生产者会被分配一段独享的空间;
3. 生产者写入元素,写入元素的同时设置available Buffer里面相应的位置,以标记自己哪些位置是已经写入成功的。
多线程写数据的情况下,主要会有并发问题,即多个线程重复写同一个元素,Disruptot的解决方式是每个线程从数组中截取一段不同的位置,并通过CAS进行写数据的操作。
但该处理方式会产生一个新的问题,就是在消费者进行消息读取的时候,可能会读到一个还没有写入的元素,为了解决这个问题,Disruptor引入了一个和RingBuffe相同大小的available buffer,它的作用是当写入成功后,会在available相应的位置标记为成功,在读取元素的时候会先遍历available buffer,只有当写入成功的位置才能访问。
1. 申请读取到序号n;
2. 若writer cursor >= n,这时仍然无法确定连续可读的最大下标。从reader curso开始读取available Buffer,一直查到第一个不可用的元素,然后返回最大连续可读元素的位置;
3. 消费者读取元素;