文章目录
- 一、Semphore&Exchanger
- 二、CountDownLatch&CyclicBarrier
- 三、Phaser
- 四、Executor& ThreadPoolExcecutor
- 五、Future&Callable
- 六、CompletionService
- 七、ExecutorService的API
- 7.1 API overview
- 7.2 invokeAny
- 1、invokeAny(Collection tasks) 与interrupted状态
- 2、invokeAny(Collection tasks) 与 执行慢的任务异常
- 3、invokeAny(Collection tasks, timeout ,timeUnits)
- 7.3 invokeAll
- 八、ScheduledExecutorService API
- 九、Fork-Join
- 十、并发集合
- 十一、总结
题记:书到用时方恨少,钱到月底不够花!
此文是读取一些关于并发工具、并发类的框架(多是入门级别)的文章时,取精去粕之后的总结,不过更多应该算是备忘了。日常工作中,并发工具其实并不常用,往往碰见适用的场景时又偏偏 记不起对应的API了。本文,专治此症。
此外,在此立个flag,凡有读书,必有输出!
一、Semphore&Exchanger
二、CountDownLatch&CyclicBarrier
三、Phaser
四、Executor& ThreadPoolExcecutor
五、Future&Callable
六、CompletionService
七、ExecutorService的API
7.1 API overview
首先来概览一下,这个接口大概有哪些重要API:
- invokeAny() 与 invokeAll() 具有阻塞特性
- invokeAny() 取得第一个完成任务的结果值,当第一个任务执行完成后,调用interrupt() 中断其他任务,所以可以结合if(Thread.intterruptted())决定任务是否继续运行。
- invokeAll() 等全部线程任务执行完成后,取得全部完成任务的结果值。
7.2 invokeAny
invokeAny、invokeAll 都具有阻塞特性
1、invokeAny(Collection tasks) 与interrupted状态
- tasks执行完任意一个,才会唤醒主线程。并且会返回 第一个执行完任务的结果。
- tasks中假设有 t1 ,t2 两个任务,t1快 t2慢,则t1完成时,会给t2 状态设置为 interrupted。可在t2 中增加if(Thread.interrputed)的判断,中断 正在执行的t2线程。(见demo7.2.1)
2、invokeAny(Collection tasks) 与 执行慢的任务异常
假设 tasks 里有t1 、 t2两个任务:
-
a. 设 t1 快,t2 慢,t1正常,t2异常。默认不会在主线程输出异常,要显式在t2中抓异常。即使在t2 中显式地抓住了异常,该异常仍然不会影响到主线程的正常执行,言下之意是,主线程捕捉不到子线程抛出的异常(demo 7.2.2 & demo 7.2.3)
-
b. 设t1 快,t2 慢,t1 异常,t2 正常,则t1 的异常仍然不能显示到控制台,而需要显式try catch,同时会等待慢的任务t2返回结果值。 假如t1 中显式 try catch 了后没有重新将这个异常抛出,则仍将返回 t1 的结果,因为在主线程看来,并没有收到 t1 异常的消息(这个异常被吞掉了)。
注:先出现异常,但不影响后面任务取值的原理在于源码中一直判断(死循环)有无正确的返回值,若到最后都无返回值则抛出异常,这个异常就是最后出现的异常。参见: AbstractExcecutorService method: doInvokeAny()
//todo 源码分析 -
c. 设tasks 里有三个任务A、B、C,设A B C 一起执行,都有异常,最终的异常就是最后一个出现的异常。(demo 7.2.4)
总结一下:tasks 任务中假如有正常的,则返回最后一个正常任务的结果;
假如都是异常的,则主线程也会抛出异常。
假如任务中try catch 中显式地 捕获了异常,却没有再次往上抛,则该任务对主线程来说是正常的(相当于异常被“吞掉”了)。
3、invokeAny(Collection tasks, timeout ,timeUnits)
在指定的时间内取得第一个先执行完任务的结果值。假如在指定时间内没能执行完任务,将会抛出异常。超时时,执行中的任务状态会变成“interrupted”,所以可以结合
if(Thread.current().isInterrupted) {
throw new InterruptionExceptioin()
}
来使运行中的线程通过抛异常中断。
再提出一个具体的场景:假如一个任务在执行时既出现了异常,又超时了,会出现什么现象呢?
答曰:主线程超时后会抛出异常,中断主线程的继续执行,所以不能获取任务的执行结果;主线程抛出异常后,子任务会在执行完毕后被interrupt。不过需要注意的是,如果希望记录子线程的异常,需要主动try catch ,若只是简单throw 出去,是打印不了异常栈轨的。//demo 7.2.5
// ===================下面都是代码demo====================
// demo7.2.1
public class MyExecutorService {
public static void main(String[] args) throws ExecutionException, InterruptedException {
List<Callable<String>> list = new ArrayList<>();
list.add(new MyCallableA());
list.add(new MyCallableB1());
ExecutorService executorService = Executors.newFixedThreadPool(10);
// invokeAny --> 只取最先完成任务的结果值
// 从结果来看就是:打印出 any 以后,主线程继续运行,而 MyCallableB1 线程继续执行完
// invokeAny --> 此方法具有阻塞特性,主线程会在此阻塞
String any = executorService.invokeAny(list);
System.out.println("invokeAny method gets value " + any);
System.out.println("main thread ends here!");
}
}
class MyExecutorService2{
public static void main(String[] args) throws ExecutionException, InterruptedException {
List<Callable<String>> list = new ArrayList<>();
list.add(new MyCallableA());
list.add(new MyCallableB2());
ExecutorService executorService = Executors.newFixedThreadPool(10);
// invokeAny --> 只取最先完成任务的结果值
// 从结果来看就是:打印出 any 以后,主线程继续运行,而 MyCallableB2 线程继续执行完
//而由于 MyCallableB2 中会对 当前线程状态做出判断,假如是已标记为“ interrupted” ,就
// 抛出异常中断,需要注意的是: MyCallableB2 中断后,异常并不会传递到 主线程
// invokeAny --> 此方法具有阻塞特性,主线程会在此阻塞
String any = executorService.invokeAny(list);
System.out.println("invokeAny method gets value " + any);
System.out.println("main thread ends here!");
}
}
class MyCallableA implements Callable<String>{
@Override
public String call() throws Exception {
System.out.println("MyCallableA begins " + System.currentTimeMillis());
IntStream.range(0, 10_00).forEach(e -> {
Math.random();Math.random();Math.random();
System.out.println("MyCallableA " + (e + 1));
});
System.out.println("MyCallableA ends " + System.currentTimeMillis());
return " return A ";
}
}
class MyCallableB1 implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println("MyCallableB1 begins " + System.currentTimeMillis());
IntStream.range(0, 20_00).forEach(e -> {
Math.random();Math.random();Math.random();
System.out.println("MyCallableB1 " + (e + 1));
});
System.out.println("MyCallableB1 ends " + System.currentTimeMillis());
return "return B1 ";
}
}
class MyCallableB2 implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println("MyCallableB2 begins " + System.currentTimeMillis());
for (int e = 0; e < 300_000; e++) {
if (!Thread.currentThread().isInterrupted()) {
Math.random();Math.random();Math.random();
System.out.println("MyCallableB2 " + (e + 1));
} else {
System.out.println(" 抛异常了,中断了!");
throw new InterruptedException("中断了");
}
}
System.out.println("MyCallableB2 ends " + System.currentTimeMillis());
return "return B2";
}
}
// demo 7.2.2
class MyExecutorService3{
public static void main(String[] args) {
Callable<String> callable1 = () -> {
System.out.println("callable1 " + Thread.currentThread().getName() +
" begins " + System.currentTimeMillis());
IntStream.range(0, 1_000).forEach(e -> {
Math.random();Math.random();Math.random();
System.out.println(" callable1 running " + (e + 1));
});
System.out.println("callable1 " + Thread.currentThread().getName() +
" ends " + System.currentTimeMillis());
return " callable1";
};
Callable<String> callable2 = () -> {
System.out.println("callable2 " + Thread.currentThread().getName() +
" begins " + System.currentTimeMillis());
IntStream.range(0, 1_0000).forEach(e -> {
Math.random();Math.random();Math.random();Math.random();
System.out.println(" callable2 running " + (e + 1));
});
if (true) {
// 此处抛出的NPE不会在主线程打印出来。必须显式地 try catch,才能捕捉到异常,打印到控制台
System.out.println("===开始中断 callable2 ====");
throw new NullPointerException("被抛出的NPE");
}
System.out.println("callable2 " + Thread.currentThread().getName() +
" ends " + System.currentTimeMillis());
return " callable2 ";
};
ArrayList<Callable<String>> callables = Lists.newArrayList(callable1, callable2);
ExecutorService executorService = Executors.newFixedThreadPool(10);
try {
String any = executorService.invokeAny(callables);
System.out.println(" 获取 执行结果值: " + any);
System.out.println(" 主线程继续执行 ");
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println(" main 1 ");
} catch (ExecutionException e) {
e.printStackTrace();
System.out.println(" main 2 ");
}
}
}
// demo 7.2.3
class MyExecutorService4{
public static void main(String[] args) {
Callable<String> callable1 = () -> {
System.out.println("callable1 " + Thread.currentThread().getName() +
" begins " + System.currentTimeMillis());
IntStream.range(0, 1_000).forEach(e -> {
Math.random();Math.random();Math.random();
System.out.println(" callable1 running " + (e + 1));
});
System.out.println("callable1 " + Thread.currentThread().getName() +
" ends " + System.currentTimeMillis());
return " callable1";
};
// 显式 try catch ,将错误捕捉到
Callable<String> callable2 = () -> {
try {
System.out.println("callable2 " + Thread.currentThread().getName() +
" begins " + System.currentTimeMillis());
IntStream.range(0, 1_0000).forEach(e -> {
Math.random();Math.random();Math.random();Math.random();
System.out.println(" callable2 running " + (e + 1));
});
if (true) {
// 此处抛出的NPE不会在主线程打印出来。必须显式地 try catch,才能捕捉到异常,打印到控制台
System.out.println("===开始中断 callable2 ====");
throw new NullPointerException("被抛出的NPE");
}
System.out.println("callable2 " + Thread.currentThread().getName() +
" ends " + System.currentTimeMillis());
} catch (NullPointerException e) {
e.printStackTrace();
// =======捕捉到 异常之后,再主动抛出去=====
throw e;
}
return " callable2 ";
};
ArrayList<Callable<String>> callables = Lists.newArrayList(callable1, callable2);
ExecutorService executorService = Executors.newFixedThreadPool(10);
try {
String any = executorService.invokeAny(callables);
System.out.println(" 获取 执行结果值: " + any);
System.out.println(" 主线程继续执行 ");
// ====主线程里无法 catch 到 子线程抛出的异常(线程间不直接通信)
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println(" main 1 ");
} catch (ExecutionException e) {
e.printStackTrace();
System.out.println(" main 2 ");
} catch (Exception e) {
e.printStackTrace();
System.out.println(" main 3 ");
}
}
}
//demo :7.2.4
class MyExecutorService5 {
public static void main(String[] args) {
// 快
Callable<String> callable1 = () -> {
System.out.println("callable1 " + Thread.currentThread().getName() +
" begins " + System.currentTimeMillis());
IntStream.range(0, 1_000).forEach(e -> {
Math.random();Math.random();Math.random();
System.out.println(" callable1 running " + (e + 1));
});
if (true) {
System.out.println(" ==== 开始中断 callable1 === ");
throw new RuntimeException("被抛出的NPE callable1 ");
}
System.out.println("callable1 " + Thread.currentThread().getName() +
" ends " + System.currentTimeMillis());
return " callable1";
};
// 慢
Callable<String> callable2 = () -> {
System.out.println("callable2 " + Thread.currentThread().getName() +
" begins " + System.currentTimeMillis());
IntStream.range(0, 1_0000).forEach(e -> {
Math.random();Math.random();Math.random();Math.random();
System.out.println(" callable2 running " + (e + 1));
});
if (true) {
// 此处抛出的NPE不会在主线程打印出来。必须显式地 try catch,才能捕捉到异常,打印到控制台
System.out.println("===开始中断 callable2 ====");
throw new NullPointerException("被抛出的NPE callable2 ");
}
System.out.println("callable2 " + Thread.currentThread().getName() +
" ends " + System.currentTimeMillis());
return " callable2 ";
};
ArrayList<Callable<String>> callables = Lists.newArrayList(callable1, callable2);
ExecutorService executorService = Executors.newFixedThreadPool(10);
try {
// 由于两个 子任务都抛出了异常,主线程实际上也会抛异常
String any = executorService.invokeAny(callables);
System.out.println(" 获取 执行结果值: " + any);
System.out.println(" 主线程继续执行 ");
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println(" main 1 ");
} catch (ExecutionException e) {
e.printStackTrace();
// 进入了这个方法块!主线程 捕捉到了 子线程的异常!
System.out.println(" main 2 ");
} catch (Exception e) {
e.printStackTrace();
System.out.println(" main 3 ");
}
}
}
// demo 7.2.5
class MyExecutorService6{
public static void main(String[] args) {
Callable<String> callable1 = () -> {
System.out.println("callable1 " + Thread.currentThread().getName() +
" begins " + System.currentTimeMillis());
IntStream.range(0, 1_234).forEach(e -> {
Math.random();Math.random();Math.random();Math.random();Math.random();
System.out.println(" callable1 running " + (e + 1));
});
if (true) {
System.out.println(" ==== 开始中断 callable1 === ");
// 注: 假如此处不使用 try catch 抓住异常,那么抛出的异常是不会在控制台显示的
throw new NullPointerException("被抛出的NPE callable1 ");
}
System.out.println("callable1 " + Thread.currentThread().getName() +
" ends " + System.currentTimeMillis());
return " callable1";
};
ExecutorService executorService = Executors.newFixedThreadPool(10);
try {
//
String any = executorService.invokeAny(Lists.newArrayList(callable1), 10 , TimeUnit.MILLISECONDS);
System.out.println("获取 任务结果" + any);
System.out.println("---------");
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println(" main A ");
} catch (ExecutionException e) {
e.printStackTrace();
System.out.println(" main B ");
} catch (TimeoutException e) {
// ======主线程会进入本代码块,表示 主线程已经抛出了异常,try 中的任务结果不能再获取了=====
e.printStackTrace();
System.out.println("main C");
}
}
}
7.3 invokeAll
2、List<Future> invokeAll(Collection tasks)
tasks 执行完所有的,才会唤醒主线程。并且会返回每一个执行完的结果。