1.继承Thread类
class MyThread extends Thread {
@Override
public void run() {
// 线程执行的代码
}
}
// 创建并启动线程
MyThread myThread = new MyThread();
myThread.start();
继承Thread类, 并重写run方法, 然后创建该类的实例并调用start方法启动线程
2.实现Runnable接口
class MyRunnable implements Runnable {
@Override
public void run() {
// 线程执行的代码
}
}
// 创建并启动线程
Thread myThread = new Thread(new MyRunnable());
myThread.start();
实现Runnable接口, 并重写run方法, 创建Thread实例并传递Runnable实例,最后调用start方法
3.实现Callable接口, 并实现
class MyCallable implements Callable<Integer>{
@Override
public Integer call() throws Exception {
return 1;
}
}
// 创建线程池, 并启动
MyCallable myCallable= new MyCallable();
ExecutorService executorService = Executors.newFixedThreadPool(10);
Future<Integer> future = executorService.submit(myCallable);
Lambda表达式
因为Runnable和Callable都是函数式接口, 所以都可以使用Lambda表达式进行简化
Runnable runable = () -> {
// 无返回值
};
Callable callable = () -> {
// 有返回值
return 1;
};
总结:
上面三种创建线程的方式, 其中继承Thread类
或者实现Runnable接口
都可以创建线程, 但是它两有一个共同的问题: 没有返回值
, 没有返回值, 就无法获取线程执行完的结果
JDK1.5新增了一个Callable接口
来解决上面的问题, 但是Callable
只能在线程池中提交任务使用
继承Thread类和实现Runnable接口两种方式本身就是一种方式,通过创建Thread实例,然后调用start()方法来创建实例
Callable接口的方式实质上也是通过Thread类来实现的, 我们可以看一下ExecutorService的submit()方法
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask);
return ftask;
}
在这个方法中, 首先将Callable实例封装成一个FutureTask实例, FutureTask实现了RunnableFuture接口,而RunnableFuture又实现了Runnable接口,也就是说封装后的FutureTask仍然只是一个任务实例,此时与线程并没有任何关系,真正建立关系是在execute()方法中
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
……
}
其中的addWorker方法, 该方法是创建一个线程
private boolean addWorker(Runnable firstTask, boolean core) {
……
w = new Worker(firstTask);
final Thread t = w.thread;
……
}
其中初始化Worker的方式如下
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
public Thread newThread(Runnable r) {
Thread t = new Thread(group, r,
namePrefix + threadNumber.getAndIncrement(),
0);
if (t.isDaemon())
t.setDaemon(false);
if (t.getPriority() != Thread.NORM_PRIORITY)
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
从上面对Callable的分析,我们可以得出结论,所有创建线程的方式都可以归结为一种方式,那就是创建Thread实例
Future主要是配合Callable使用
Callable的call方法可以有返回值,可以声明抛出异常。和Callable配合的有一个Future类,通过Future可以了解任务执行情况,或者取消任务的执行,还可获取任务执行的结果,这些功能都是Runnable做不到的,Callable的功能要比Runnable强大。
Future<String> future = Executors.newSingleThreadExecutor().submit(() -> {
return "Task completed";
});
1.取消任务
用于取消任务的执行。 参数true表示中断执行任务的线程
boolean canceled = future.cancel(true);
2.检查任务是否完成
用于检查任务是否已经完成。如果任务已经完成,返回true;否则返回false。
if (future.isDone()) {
// 任务已完成
} else {
// 任务未完成
}
3.处理异常
get()方法用于获取异步任务的结果。如果任务已经完成,它会立即返回结果;否则,它会阻塞直到任务完成。
如果在执行过程中, 线程抛出异常, 使用 try-catch 块来处理异步任务中的异常。
try {
String result = future.get();
System.out.println(result);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
Callable+Future
虽然可以实现多个task并行执行,但是如果遇到前面的task执行较慢时需要阻塞等待前面的task执行完后面task才能取得结果。
而CompletionService的主要功能就是一边生成任务,一边获取任务的返回值。让两件事分开执行,任务之间不会互相阻塞,可以实现先执行完的先取结果,不再依赖任务顺序了。
案例: Future方式
public static void main(String[] args) {
// 创建线程池
ExecutorService executor = Executors.newFixedThreadPool(2);
// 异步向电商S1询价
Future<Integer> f1 = executor.submit(() -> getPriceByS1());
// 异步向电商S2询价
Future<Integer> f2 = executor.submit(() -> getPriceByS2());
// 获取电商S1报价并异步保存
executor.execute(() -> save(f1.get()));
// 获取电商S2报价并异步保存
executor.execute(() -> save(f2.get()));
}
在这个案例中, 如果获取S1报价的耗时很长, 那么即使获取S2报价的耗时很短, 也无法让保存S2报价的操作先执行, 因为此时主线程会阻塞在f1.get()操作上
案例: CompletionService方式
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService executor = Executors.newFixedThreadPool(10);
//创建CompletionService
CompletionService<Integer> cs = new ExecutorCompletionService<>(executor);
//异步向电商S1询价
cs.submit(() -> {
Thread.sleep(2000);
return 10;
});
//异步向电商S2询价
cs.submit(() -> {
return 20;
});
//将询价结果异步保存到数据库
for (int i = 0; i < 2; i++) {
Integer r = cs.take().get();
System.out.println(r);
}
}
CompletionService更适合处理一组任务, 可以将所有的任务提交到 CompletionService 中,然后按照它们完成的顺序逐个处理结果。
调用take()方法会阻塞等待下一个已完成的任务,这意味着你可以立即处理完成的任务,而不必等待所有任务都完成。
CompletionService内部通过阻塞队列
+ FutureTask
, 阻塞队列的作用是为了存储那些已经执行完成的任务的Future对象, 它是按照完成先后顺序排序
CompletableFuture
CompletableFuture是对Future
的扩展和增强, 它实现了Future
接口, 并在此基础上进行了丰富的扩展,完美弥补了Future的局限性
CompletableFuture提供了4个静态方法来创建异步操作
public static CompletableFuture<Void> runAsync(Runnable runnable)
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)
runAsync和supplyAsync的区别
runAsync | supplyAsync | |
---|---|---|
参数 | 接受一个Runnable参数,表达异步执行的任务, 没有返回值 | 接受一个Supplier参数, 表达异步执行的任务, 且有一个返回值 |
返回值类型 | CompletableFuture |
CompletableFuture,其中 U 是通过 Supplier 提供的结果类型 |
结果处理 | 任务执行完毕后,不返回任何结果,因此runAsync不会有返回值。 | 任务执行完毕后,会返回一个结果。 |
用途 | 适用于那些不需要返回结果的异步任务,比如异步地执行一些操作但不需要返回任何值。 | 适用于那些需要返回结果的异步任务,比如异步地获取某个值。 |
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
// 异步执行的任务
System.out.println("Task running asynchronously");
});
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
// 异步执行的任务,并返回结果
return "Result of the asynchronous computation";
});
Executor参数的作用
使用没有指定Executor
的方法时,内部使用ForkJoinPool.commonPool()
作为它的线程池执行异步代码。如果指定线程池,则使用指定的线程池运行。
ForkJoinPool.commonPool(), 这个线程池默认创建的线程数是 CPU 的核数(也可以通过 JVM option:-Djava.util.concurrent.ForkJoinPool.common.parallelism 来设置ForkJoinPool线程池的线程数)
如果所有CompletableFuture共享一个线程池,那么一旦有任务执行一些很慢的 I/O 操作,就会导致线程池中所有线程都阻塞在 I/O 操作上,从而造成线程饥饿,进而影响整个系统的性能。所以,强烈建议你要根据不同的业务类型创建不同的线程池,以避免互相干扰
join()
和get()
方法都是用来获取CompletableFuture异步之后的返回值。
join() | get() | |
---|---|---|
抛出异常 | join()方法不会抛出受检查异常,因此在使用时不需要处理异常,这使得代码更加清晰简洁。 | get()方法会抛出 InterruptedException 和 ExecutionException 异常,因此需要进行异常处理。在实际应用中,通常需要处理这些异常,以确保对异步任务的正确处理。 |
总的来说,如果你不需要处理异常,并且可以确保异步任务不会抛出异常,那么使用 join() 方法可能更为方便。如果需要处理异常或者希望更细粒度地控制异常的处理,可以选择使用 get() 方法。
依赖关系
and集合关系
or聚合关系
并行执行
结果处理
public CompletableFuture<T> whenComplete(BiConsumer<? super T,? super Throwable> action)
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T,? super Throwable> action)
public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T,? super Throwable> action, Executor executor)
whenComplete用于处理异步结果
或异常
, 它允许你注册一个回调函数, 这个函数会在异步计算完成后(无论是正常完成还是出现异常)后执行
whenComplete方法不阻塞线程,它是异步执行的。
action参数是一个BiConsumer,接受两个参数:计算的结果
(如果成功完成)和异常
(如果异步任务抛出了异常)。
import java.util.concurrent.CompletableFuture;
public class WhenCompleteExample {
public static void main(String[] args) {
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
// Simulate some computation
int i = 1 / 0;
return "Hello, CompletableFuture!";
}).whenComplete((result, exception) -> {
if (exception == null) {
System.out.println("Result: " + result);
} else {
System.out.println("Exception: " + exception.getMessage());
}
});
future.get();
}
}
在这个例子中,whenComplete 方法注册了一个回调函数,该函数会在异步计算完成时被调用。回调函数根据计算结果是否异常分别进行处理,输出相应的信息。
需要注意的是,whenComplete 方法不会阻塞主线程,因此在主线程继续执行其他工作之前,我们通过 Thread.sleep 确保异步任务有足够的时间完成。在实际应用中,通常会使用更复杂的控制流或者 CompletableFuture.join() 方法等方式来等待异步任务的完成。
whenComplete和whenCompleteAsync的区别
public static void main(String[] args) {
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
System.out.println("Async Thread: " + Thread.currentThread().getName());
return "Hello, CompletableFuture!";
});
// 使用 whenComplete
CompletableFuture whenCompleteFuture = future.whenComplete((result, exception) -> {
System.out.println("whenComplete Thread: " + Thread.currentThread().getName());
});
// 使用 whenCompleteAsync
CompletableFuture whenCompleteAsyncFuture = future.whenCompleteAsync((result, exception) -> {
System.out.println("whenCompleteAsync Thread: " + Thread.currentThread().getName());
});
// Ensure the program doesn't exit before the CompletableFutures complete
CompletableFuture.allOf(whenCompleteFuture, whenCompleteAsyncFuture)
.join();
}
whenComplete
的回调函数在异步任务的执行线程上执行,因此在输出中你会看到Async Thread
和whenComplete Thread
的线程名称是一致的。whenCompleteAsync
的回调函数会在默认的ForkJoinPool中的某个线程上执行,因此whenCompleteAsync Thread
的线程名称可能不同于Async Thread。
public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn)
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn)
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn, Executor executor)
它的使用方法基本与whenComplete一致, 不过有以下几个区别
thenApply | whenComplete | |
---|---|---|
处理方式 | 用于对异步任务的正常结果进行处理 | 用于在异步任务完成时执行回调,不论是正常结果还是异常 |
返回值类型 | 返回一个新的 CompletableFuture,该 CompletableFuture 包含了对原始结果应用函数的结果 | 返回一个新的 CompletableFuture,该 CompletableFuture 在原始 CompletableFuture 完成时触发回调函数,但不影响结果的值。 |
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
return "Hello, CompletableFuture!";
}).thenApply(result -> {
try {
Thread.sleep(5000);
System.out.println("thenApply begin!");
return 1;
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
System.out.println("method over!");
执行结果:
thenApply begin!
method over!
在上文中, 我们了解到whenComplete、thenApply并不会阻塞主线程, 但是在这个案例中, 我们发现thenApply方法的执行却阻塞了主线程
原因: supplyAsync中方法执行太快了, 在调用thenApply方法时, 前一个异步线程已经回收了
thenApply
中的函数执行的线程是取决于前一个 CompletableFuture 阶段的执行线程,而不是直接在调用 thenApply 的线程中执行。
我们可以在supplyAsync中让线程sleep一会儿, 让这个future有时间调用thenApply, 我们可以看看结果
public <U> CompletableFuture<U> thenCompose(Function<? super T, ? extends CompletionStage<U>> fn)
public <U> CompletableFuture<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn)
public <U> CompletableFuture<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn, Executor executor)
thenCompose 方法用于组合两个异步操作, 把其中一个操作的结果作为另一个操作的输入参数, 该函数返回一个新的CompletableFuture,该CompletableFuture代表两个阶段的组合
public static void main(String[] args) throws ExecutionException, InterruptedException {
// supplyAsync
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<Integer> result = future.thenApply(s -> {
System.out.println("Transforming Thread: " + Thread.currentThread().getName());
return s.length();
});
// thenCompose
CompletableFuture<String> future_2 = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<Integer> result_2 = future_2.thenCompose(s -> {
System.out.println("Transforming Thread: " + Thread.currentThread().getName());
return CompletableFuture.supplyAsync(() -> s.length());
});
}
thenCompose和thenApply的区别
thenCompose | thenApply | |
---|---|---|
作用 | 对异步操作的结果进行转换 | 对异步操作的结果进行转换 |
参数 | 参数是一个Function | 参数是一个Function |
转换函数 | 返回一个 CompletableFuture | 返回一个值 |
thenCompose 在需要多个异步操作进行串联时更为灵活,可以避免出现嵌套的 CompletableFuture 结构。
thenCompose的优点
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<Integer> result = future.thenCompose(s -> {
System.out.println("Transforming Thread: " + Thread.currentThread().getName());
return CompletableFuture.supplyAsync(() -> s.length())
.thenCompose(len -> CompletableFuture.supplyAsync(() -> len * 2));
});
在这个例子中,thenCompose 允许我们在转换函数中定义另一个异步操作,而不是只能返回简单的值。
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<Integer> result = future.thenCompose(s -> {
System.out.println("Transforming Thread: " + Thread.currentThread().getName());
return CompletableFuture.supplyAsync(() -> s.length());
});
在这个例子中,thenCompose 返回的 CompletableFuture 直接包含了最终的转换结果,而不是嵌套的 CompletableFuture。
总的来说,thenCompose 更适合处理多个异步操作的组合,特别是当你需要根据前一个阶段的结果定义后续的异步操作时,以及希望结果扁平化的情况。
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 方式一: 使用supplyAsync
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
int i = 1 / 0;
return "test";
}).exceptionally(new Function<Throwable, String>() {
@Override
public String apply(Throwable t) {
System.out.println("执行失败:" + t.getMessage());
return "异常xxxx";
}
});
future.get();
// 方式二: 使用runAsync
CompletableFuture.runAsync(() -> {
int i = 1 / 0;
}).exceptionally((throwable -> {
System.out.println("执行失败:" + throwable.getMessage());
return null;
}));
}
链式编程导致的差异
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 链式编程
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
int i = 1 / 0;
return "test";
}).exceptionally(new Function<Throwable, String>() {
@Override
public String apply(Throwable t) {
System.out.println("执行失败:" + t.getMessage());
return "异常xxxx";
}
});
future.get();
// 普遍编程
CompletableFuture<String> future_2 = CompletableFuture.supplyAsync(() -> {
int i = 1 / 0;
return "test";
});
future_2.exceptionally(new Function<Throwable, String>() {
@Override
public String apply(Throwable t) {
System.out.println("执行失败:" + t.getMessage());
return "异常xxxx";
}
});
future_2.get();
}
我们发现future.get()
并没有抛异常, 而future_2.get()
抛出了异常
这是因为exceptionally方法返回了一个新的CompletableFuture, 它包装了原始的CompletableFuture, 新的CompletableFuture将使用指定的异常处理函数来计算结果,而不会抛出原始的异常
而future_2.get()
抛出异常,是因为你在原始的CompletableFuture上调用了exceptionally方法,但并没有使用返回的新的CompletableFuture。exceptionally方法返回的新CompletableFuture并没有被存储在变量中,因此对future_2.get()
的调用仍然会抛出原始的异常。
结果消费只对结果执行action, 而不返回新的计算值
public CompletableFuture<Void> thenAccept(Consumer<? super T> action)
public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action)
public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action, Executor executor)
thenAccept | thenApply | |
---|---|---|
目的 | 消费结果 | 转换结果 |
参数 | Consumer | Function |
返回值 | 不返回新的结果 | 产生新的结果 |
总体来说,如果你只需要在异步操作完成时执行一些操作而不关心返回的结果,可以使用 thenAccept。如果你希望对异步操作的结果进行转换,并产生新的结果,可以使用 thenApply。
用于组合两个独立的异步操作,并在两者都完成时执行一个消费者操作。
public <U> CompletionStage<Void> thenAcceptBoth(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action);
public <U> CompletionStage<Void> thenAcceptBothAsync(CompletionStage<? extends U> other,BiConsumer<? super T, ? super U> action);
public static void main(String[] args) {
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> " World");
// 在两个异步操作都完成时执行消费者函数
CompletableFuture<Void> result = future1.thenAcceptBoth(future2, (s1, s2) -> {
System.out.println("Consumer Thread: " + Thread.currentThread().getName());
System.out.println("Result 1: " + s1);
System.out.println("Result 2: " + s2);
});
// 等待结果
result.join();
}
在这个例子中,通过两个独立的异步任务 future1 和 future2 分别生成字符串 “Hello” 和 " World"。然后,使用 thenAcceptBoth 将这两个异步操作组合在一起,通过 BiConsumer 函数 (s1, s2) -> {…} 处理它们的结果。
需要注意的是,thenAcceptBoth 只有在两个 CompletableFuture 都完成时才会执行传入的消费者函数。如果其中一个 CompletableFuture 未完成,那么 thenAcceptBoth 也不会执行。
public CompletionStage<Void> thenRun(Runnable action);
public CompletionStage<Void> thenRunAsync(Runnable action);
thenRun与thenApply相比, 它不需要对异步操作的结果进行处理, 它只关心异步操作是否完成
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello");
// 在异步操作完成时执行一段操作,不关心结果
CompletableFuture<Void> result = future.thenRun(() -> {
System.out.println("Runnable Thread: " + Thread.currentThread().getName());
System.out.println("Operation completed");
});
总的来说,thenRun 用于在异步操作完成时执行一段操作而不关心结果,而 thenApply 用于在异步操作完成时对结果进行处理并返回新的结果。选择使用哪个方法取决于你的需求,是关心结果还是只关心操作完成与否。
用于在两个独立的异步操作都完成时执行一段操作,该操作不关心异步操作的结果,也不返回新的结果
import java.util.concurrent.CompletableFuture;
public class RunAfterBothExample {
public static void main(String[] args) {
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> " World");
// 在两个异步操作都完成时执行一段操作,不关心结果
CompletableFuture<Void> result = future1.runAfterBoth(future2, () -> {
System.out.println("Runnable Thread: " + Thread.currentThread().getName());
System.out.println("Both operations completed");
});
// 主线程继续执行其他操作
System.out.println("Main Thread: Doing something else...");
// 防止主线程过早退出
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public <U,V> CompletableFuture<V> thenCombine(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn);
public <U,V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn);
public <U,V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn, Executor executor);
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> " World");
// 组合两个异步操作的结果,并返回新的结果
CompletableFuture<String> result = future1.thenCombine(future2, (s1, s2) -> s1 + s2);
thenCombine和thenAcceptBoth的区别
thenCombine
和thenAcceptBoth
都是 CompletableFuture 类中用于组合两个独立的异步操作的方法,它们之间的主要区别在于返回值和使用场景。
thenCombine | thenAcceptBoth | |
---|---|---|
作用 | 组合两个独立的异步操作 | 组合两个独立的异步操作 |
参数 | BiFunction函数, 接受两个异步操作的结果,返回新的结果 | BiConsumer函数, 接受两个异步操作的结果,但不返回新的结果 |
使用场景 | 适用于需要对两个异步操作的结果进行组合操作,产生新的结果的场景。 | BiConsumer函数, 接受两个异步操作的结果,但不返回新的结果适用于需要在两个异步操作都完成时执行一些操作的场景,而不关心返回的结果 |
总体来说,thenCombine 用于产生新的结果,而 thenAcceptBoth 用于在两个异步操作都完成时执行一些操作而不产生新的结果。选择使用哪个方法取决于你的需求,是需要组合新的结果还是只关心执行一些操作。
任务竞速指两个线程任务获取结果的速度相比较,按一定的规则进行下一步处理。
两个线程任务相比较,先获得执行结果的,就对该结果进行下一步的转化操作。
public <U> CompletionStage<U> applyToEither(CompletionStage<? extends T> other,Function<? super T, U> fn);
public <U> CompletionStage<U> applyToEitherAsync(CompletionStage<? extends T> other,Function<? super T, U> fn);
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return "Hello";
});
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return "Hi";
});
CompletableFuture<String> result = future1.applyToEither(future2, t -> t + " world");
System.out.println(result.get());
两个线程任务相比较,先获得执行结果的,就对该结果进行下一步的消费操作
public CompletionStage<Void> acceptEither(CompletionStage<? extends T> other,Consumer<? super T> action);
public CompletionStage<Void> acceptEitherAsync(CompletionStage<? extends T> other,Consumer<? super T> action);
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return "Hello";
});
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return "Hi";
});
CompletableFuture result = future1.acceptEither(future2, t -> System.out.println(t));
result.get();
public CompletionStage<Void> runAfterEither(CompletionStage<?> other,Runnable action);
public CompletionStage<Void> runAfterEitherAsync(CompletionStage<?> other,Runnable action);
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return "Hello";
});
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return "Hi";
});
CompletableFuture result = future1.runAfterEither(future2, () -> System.out.println("已经有一个任务完成了"));
result.get();
public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs)
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return "Hello";
});
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return "Hi";
});
CompletableFuture<Void> allOf = CompletableFuture.allOf(future1, future2);
allOf.get();
allOf和get的区别
allOf用于等待一组CompletableFuture完成,而get用于获取单个 CompletableFuture 的结果。在使用get方法时要小心,因为它可能会导致线程阻塞,应谨慎使用以避免可能的死锁或性能问题。
anyOf()的参数是多个给定的CompletableFuture,当其中的任何一个完成时,方法返回这个CompletableFuture。
public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs)
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return "Hello";
});
CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return 6;
});
CompletableFuture<Object> anyOf = CompletableFuture.anyOf(future1, future2);
System.out.println(anyOf.join());