IINDEX
CountDownLatch
字面直译
减数门闩
作用
让一些线程阻塞,直到另一些线程都执行完成
相当于一扇门,这个门上插了 n 根门闩,new CountDownLatch(n)
每个门闩有个人在往外拉(在干点什么任务的线程),没拉出来一根,门闩总数减 1
当所有门闩都拉出来,门闩总数是 0 了,就可以开门了
作用示意
使用示例
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(10);
new Thread(()->{
try {
latch.await();
System.out.println("A Sth on next step");
} catch (InterruptedException e) {
e.printStackTrace();
}
},"A").start();
new Thread(()->{
try {
latch.await();
System.out.println("B Sth on next step");
} catch (InterruptedException e) {
e.printStackTrace();
}
},"B").start();
for(int i = 0; i < 10; i++) {
new Thread(()->{
try {
TimeUnit.MILLISECONDS.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" over");
latch.countDown();
},String.valueOf(i)).start();
}
}
CyclicBarrier
字面直译
循环屏障
作用
让一组线程达到一个屏障(同步点)时被阻塞,直到最后一个线程达到屏障
此后屏障才会放行,各个线程才能做后面的任务
作用示意
使用示例
public static void main(String[] args) {
CyclicBarrier barrier = new CyclicBarrier(5,()->{System.out.println("All Is Ready");});
for(int i = 0; i < 5; i++) {
new Thread(()->{
try {
System.out.println(Thread.currentThread().getName()+" arrived");
TimeUnit.MILLISECONDS.sleep(100);
barrier.await();
} catch (BrokenBarrierException | InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" pass");
},String.valueOf(i)).start();
}
}
Semaphore
字面直译
信号灯
作用
- 使多个共享资源互斥使用
- 控制并发线程数
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(4);
for(int i = 0; i < 7; i++) {
new Thread(()->{
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName()+" <-");
TimeUnit.MILLISECONDS.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
System.out.println(Thread.currentThread().getName()+" ---->");
semaphore.release();
}
}, String.valueOf(i)).start();
}
}
ForkJoinPool / ForkJoinTask / RecursiveTask
public class ForkJoinDemo extends RecursiveTask<Integer>{
private int start;
private int end;
public ForkJoinDemo(int start, int end) {
this.start = start;
this.end = end;
}
public static void main(String[] args) {
System.out.println(new ForkJoinDemo(1,100).compute());
}
@Override
protected Integer compute() {
if (start == end)
return start;
if((end-start)>15){
ForkJoinDemo demo = new ForkJoinDemo(start,start+((end-start)>>1));//start+(end-start)>>1
ForkJoinDemo demo2 = new ForkJoinDemo(start+((end-start)>>1)+1,end);
demo.fork();
demo2.fork();
return demo2.join()+ demo.join();
}
int sum = 0;
for(int i = start;i<=end;i++){
sum += i;
}
System.out.println(start+" ~ "+end+" = "+sum);
return sum;
}
}
CompletableFuture 使用
CompletableFuture
实现了 Future
和 CompletionStage
接口
CompletionStage
接口是对复杂工作中 每一个可完成的步骤 的抽象
常用方法
执行方法,可按是否有返回值、是否指定线程池归类
无线程池 | 有线程池 | |
---|---|---|
无返回值 | runAsync(Runnable) | runAsync(Runnable,Executor) |
有返回值 | supplyAsync(Supplier) | supplyAsync(Supplier,Executor |
回调方法
回调时机 | 参数 | |
---|---|---|
whenComplete((result,except)->{}) | 完成时 | 2:结果、异常 |
exceptionally((exception)->{}) | 异常时 | 1:异常 |
获取结果方法
是否立即获取 | 是否可设置超时 | 是否抛出未检查异常 | 说明 | |
---|---|---|---|---|
get() | × | √ | √ | |
join() | × | × | × | |
getNow() | √ | × | × | 传入一个参数,调用此方法但计算未完成时,返回此参数 |
主动触发计算
complete()
,尝试完成 get()
或 join()
方法
要求一个入参,调用后立即返回 get()
或 join()
方法是否被打断
未处理完时通过 get()
或 join()
方法或获取入参值
流程控制
- 流程控制方法可使前后步骤串行化
通常用于处理下一个CompletableStage
,实现异步任务的顺序执行 - 下面方法都有 异步重载,如
thenRunAsync()
- 下面方法都有 异步指定线程池重载,如
thenRunAsync(Consumer , Executor)
性质 | 接口类型 | 参数 | 返回值 | 异常时 | 说明 | |
---|---|---|---|---|---|---|
thenRun() | 执行 | Runable | 0 | × | 终止 | 只是需要在上一步骤之后执行,但与上一步结果关系不大 |
thenApply() | 处理 | Function | 1:结果 | √ | 终止 | 在上一步执行结果的基础上进一步处理 |
handle() | 处理 | BiConsumer | 2:结果、异常 | √ | 可继续 | 在上一步执行结果的基础上进一步处理 |
thenAccept() | 消费 | Function | 1:结果 | × | 终止 | 消费上一步处理结果 |
thenAccept() | 选择 | Function | 1:结果 | √ | 终止 | 与另一个 CompletableFuture 一同进行,谁快使用谁的结果,慢的会被终止,示例 3 |
thenCombine() | 合并 | BiFunction | 2:结果、另一个结果 | √ | 终止(合并时) | 与另一个 CompletableFuture 一同进行,二者都执行完后对二者结果进行合并,示例 3 |
关于异步和线程池的关系如下 (参考 示例 2)
- 定义
CompletableFuture
时- 若未指定线程池,通常使用默认的
ForkJoinPool
- 若指定线程池,通常 使用指定的线程池
- 若未指定线程池,通常使用默认的
- 使用上述流程控制方法时
- 若调用的流程控制方法,通常使用 与上一阶段一致的线程池
- 若调用的流程控制方法的 异步重载,通常使用默认的
ForkJoinPool
- 若调用的流程控制方法的 异步指定线程池重载,通常 使用指定的线程池
- 若某步骤执行极快,可能不使用线程池,直接使用调用时线程执行
- 比如下文 //1 的睡眠注释,会出现 main 线程
这是因为根据线程调度优化,优化后不在切换上下文了,于是supplyAsync
和后面的步骤可能直接使用调用线程
CompletableFuture
与其他多线程进度控制的区别
CompletableFuture
的大部分功能可以通过其他进度控制实现CompletableFuture
更强调一个复杂流程中不同步骤的控制
其他进度控制的子任务间更多的只是协同关系,而不是步骤的从属CompletableFuture
在接口设计上比其他进度控制强大
它实现了Future
、CompletionStage
,因此同时具有这些接口和其调用者提供的功能
示例 1
public class CompletableFutureDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
CompletableFuture<Void> f = CompletableFuture.runAsync(()->{
System.out.println("==============");
});
f.get();
System.out.println();
System.out.println();
CompletableFuture<Integer> f1 = CompletableFuture.supplyAsync(()->{
System.out.println("**************");
return 1;
});
f1.whenComplete((result,except)->{
System.out.println(result);
System.out.println(except);
});
System.out.println();
System.out.println();
CompletableFuture<Integer> f2 = CompletableFuture.supplyAsync(()->{
System.out.println("-------------");
return 1/0;
});
f2.whenComplete((result,except)->{
System.out.println(result);
System.out.println(except);
});
}
}
示例 2
public static void main(String[] args){
CompletableFuture.supplyAsync(()->{
// 1
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
return 1;
},Executors.newFixedThreadPool(3))
.thenRun(()->{System.out.println("1 "+Thread.currentThread().getName());})
.thenAcceptAsync(x->System.out.println("2 "+Thread.currentThread().getName()))
.thenAcceptAsync(x->System.out.println("2 "+Thread.currentThread().getName()),Executors.newFixedThreadPool(3))
.thenAcceptAsync(x->System.out.println("2 "+Thread.currentThread().getName()),Executors.newFixedThreadPool(3))
.thenAccept(x->System.out.println("2 "+Thread.currentThread().getName()))
.thenAccept(x->System.out.println("2 "+Thread.currentThread().getName()))
.whenComplete((result, except)->{
System.out.println(result);
System.out.println(except);
});
}
示例 3
public static void main(String[] args){
CompletableFuture a = CompletableFuture.supplyAsync(()->{
System.out.println("a start");
try { TimeUnit.SECONDS.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); }
return "a";
});
CompletableFuture b = CompletableFuture.supplyAsync(()->{
System.out.println("b start");
try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
return "b";
});
System.out.println(a.applyToEither(b,r-> r + " is applied : " + Thread.currentThread().getName()).join());
System.out.println(a.thenCombine(b, (x,y)->x+" | "+y +": "+ Thread.currentThread().getName()).join());
}