点击上方“程序员蜗牛g”,选择“设为星标”跟蜗牛哥一起,每天进步一点点程序员蜗牛g大厂程序员一枚 跟蜗牛一起 每天进步一点点31篇原创内容公众号
我们有一段业务,类似一个报表,就是获取用户的订单汇总,邮费汇总,各种手续费汇总,显示在页面。那么最好的方案就是多线程分别获取然后汇总到一起返回。
在Java中获取异步线程的结果通常可以使用Future
和Callable
、CompletableFuture
、FutureTask
等类来实现。
这些类可以用来提交任务到线程池,并在任务完成后获取结果。
这就是我们想要的结果,那么这里来深入研究分析一下这三个方案。
使用Future和Callable
package com.luke.designpatterns.demo;
import java.util.concurrent.*;
public class demo {
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<Integer> future = executor.submit(new Callable<Integer>() {
public Integer call() throws Exception {
// 获取各种汇总的代码,返回结果
return 42;
}
});
// 获取异步任务的结果
Integer result = future.get();
System.out.println("异步任务的结果是" + result);
executor.shutdown();
}
}
它们的原理是通过将任务提交到线程池执行,同时返回一个Future对象,该对象可以在未来的某个时刻获取任务的执行结果。
- Callable 接口: Callable 是一个带泛型的接口,它允许你定义一个返回结果的任务,并且可以抛出异常。这个接口只有一个方法
call()
,在该方法中编写具体的任务逻辑。 - Future 接口: Future 接口代表一个异步计算的结果。它提供了方法来检查计算是否完成、等待计算的完成以及检索计算的结果。
- Future 提供了一个
get()
方法,它会阻塞当前线程直到计算完成,并返回计算的结果。
Callable 接口本身并不直接启动线程,它只是定义了一个可以返回结果的任务。要启动一个
Callable
实例的任务,通常需要将其提交给ExecutorService
线程池来执行。
在 ExecutorService
中,可以使用 submit(Callable<T> task)
方法提交 Callable 任务。这个方法会返回一个 Future 对象,它可以用来获取任务的执行结果。
启动 Callable 任务的原理可以概括为以下几个步骤:
- 创建 Callable 实例: 首先需要创建一个实现了 Callable 接口的类,并在
call()
方法中定义具体的任务逻辑,包括要执行的代码和返回的结果。 - 创建 ExecutorService 线程池: 使用 Executors 类的工厂方法之一来创建一个
ExecutorService
线程池,例如newFixedThreadPool(int nThreads)
、newCachedThreadPool()
等。 - 提交任务: 将 Callable 实例通过
ExecutorService
的submit(Callable<T> task)
方法提交到线程池中执行。线程池会为任务分配一个线程来执行。 - 异步执行:
ExecutorService
线程池会在后台异步执行任务,不会阻塞当前线程,使得主线程可以继续执行其他操作。 - 获取结果: 通过 Future 对象的
get()
方法获取任务的执行结果。如果任务尚未完成,get()
方法会阻塞当前线程直到任务完成并返回结果。
总的来说,Callable 启动线程的原理是将任务提交给 ExecutorService
线程池,线程池会负责管理线程的执行,执行任务的过程是在独立的线程中进行的,从而实现了异步执行的效果。
使用CompletableFuture
import java.util.concurrent.CompletableFuture;
public class Main {
public static void main(String[] args) throws InterruptedException, ExecutionException {
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
// 获取各种汇总的代码,返回结果
return 43;
});
// 获取异步任务的结果
Integer result = future.get();
System.out.println("异步任务的结果:" + result);
}
}
CompletableFuture
是 Java 8 引入的一个类,用于实现异步编程和异步任务的组合。
它的原理是基于"Completable
"(可以完成的)和"Future
"(未来的结果)的概念,提供了一种方便的方式来处理异步任务的执行和结果处理。
CompletableFuture
的原理可以简单概括为以下几点:
- 异步执行:
CompletableFuture
允许你以异步的方式执行任务。你可以使用supplyAsync()
、runAsync()
等方法提交一个任务给CompletableFuture
执行,任务会在一个独立的线程中执行,不会阻塞当前线程。 - 回调机制:
CompletableFuture
提供了一系列的方法来注册回调函数,这些回调函数会在任务执行完成时被调用。例如,thenApply()
,thenAccept()
,thenRun()
等方法可以分别处理任务的结果、完成时的操作以及任务执行异常时的处理。 - 组合多个任务:
CompletableFuture
支持多个任务的组合,可以使用thenCombine()
、thenCompose()
、thenAcceptBoth()
等方法来组合多个任务,实现任务之间的依赖关系。 - 异常处理:
CompletableFuture
允许你对任务执行过程中抛出的异常进行处理,可以使用exceptionally()
、handle()
等方法来处理异常情况。 - 等待任务完成: 与 Future 类似,
CompletableFuture
也提供了get()
方法来等待任务的完成并获取结果。但与传统的 Future 不同,CompletableFuture
的get()
方法不会阻塞当前线程,因为任务的执行是异步的。
总的来说,CompletableFuture
的原理是基于回调和异步执行的机制,提供了一种方便的方式来处理异步任务的执行和结果处理,同时支持任务的组合和异常处理。
使用FutureTask
import java.util.concurrent.*;
public class Main {
public static void main(String[] args) throws InterruptedException, ExecutionException {
FutureTask<Integer> futureTask = new FutureTask<>(() -> {
// 获取各种汇总的代码,返回结果
return 44;
});
Thread thread = new Thread(futureTask);
thread.start();
// 获取异步任务的结果
Integer result = futureTask.get();
System.out.println("异步任务的结果:" + result);
}
}
FutureTask
是 Java 中实现 Future 接口的一个基本实现类,同时也实现了 Runnable
接口,因此可以被用作一个可运行的任务。
FutureTask
的原理是将一个可调用的任务(Callable
或 Runnable
)封装成一个异步的、可取消的任务,它提供了一个机制来获取任务的执行结果。
FutureTask
的原理可以简要概括如下:
- 封装任务:
FutureTask
接受一个Callable
或 Runnable 对象作为构造函数的参数,并将其封装成一个异步的任务。 - 执行任务:
FutureTask
实现了Runnable
接口,因此可以作为一个可运行的任务提交给Executor
(通常是ExecutorService
)来执行。当FutureTask
被提交到线程池后,线程池会在一个独立的线程中执行该任务。 - 获取结果: 通过 Future 接口的方法,可以等待任务执行完成并获取其结果。
FutureTask
实现了Future
接口,因此可以调用get()
方法来获取任务的执行结果。如果任务尚未完成,get()
方法会阻塞当前线程直到任务完成并返回结果。 - 取消任务:
FutureTask
提供了cancel(boolean mayInterruptIfRunning)
方法来取消任务的执行。可以选择是否中断正在执行的任务。一旦任务被取消,get() 方法会立即抛出CancellationException
异常。
总的来说,FutureTask
的原理是将一个可调用的任务封装成一个异步的、可取消的任务,并通过 Future 接口来提供获取任务执行结果和取消任务的机制。
这些方法中,get()
方法会阻塞当前线程,直到异步任务完成并返回结果。如果任务抛出异常,get()
方法会将异常重新抛出。
我们平时常用的方法就是这四种。。。
最后说一句(求关注!别白嫖!)
如果这篇文章对您有所帮助,或者有所启发的话,求一键三连:点赞、转发、在看。
关注公众号:woniuxgg,在公众号中回复:笔记 就可以获得蜗牛为你精心准备的java实战语雀笔记,回复面试、开发手册、有超赞的粉丝福利!