Java高并发编程中Executor、ExecutorService的使用及详细介绍-刘宇
作者:刘宇
CSDN博客地址:https://blog.csdn.net/liuyu973971883
有部分资料参考,如有侵权,请联系删除。如有不正确的地方,烦请指正,谢谢。
一、什么是Executor框架
Executor是从Java5中才开始有的,在java.util.cocurrent包下,是非常强大的异步执行框架,标准的将任务提交过程和执行过程进行解耦。内部使用了线程池机制,可以通过该框架来控制线程的启动、执行、关闭等,大大提高了并发开发的效率。
二、Executor框架示意图
- Executor:是一个基本接口,提供方法execute(Runnable command),该方法接收一个Runable实例,将提交任务的过程与执行任务的过程进行了解耦。
- ExecutorService:是一个比Executor使用更广泛的子类接口,其提供了生命周期管理的方法,以及可跟踪一个或多个异步任务执行状况返回Future的方法
- AbstractExecutorService:ExecutorService执行方法的默认实现
- ScheduledExecutorService:一个可定时调度任务的接口
- ThreadPoolExecutor:线程池,可以通过调用Executors以下静态工厂方法来创建线程池并返回一个ExecutorService对象:
- ScheduledThreadPoolExecutor:ScheduledExecutorService的实现,一个可定时调度任务的线程池
三、ExecutorService的方法详解
1、submit方法
//提交一个Runnable的任务,该任务没有返回结果,虽然返回一个Future对象,但是Future的get是null。
Future<?> submit(Runnable task)
//提交一个Runnable的任务,该任务可以有返回值,通过传入的result对象返回结果。
<T> Future<T> submit(Runnable task, T result)
//提交一个Callable的任务,该任务可以有返回值,因为Callable与Runnable不同,Callable自身就可以返回结果
<T> Future<T> submit(Callable<T> task)
2、invokeAny方法
该方法会阻塞。批量提交任务并执行所有callable,直到获得一个已经成功执行的任务的结果,如果一个任务已经完成,剩余的Callable任务将被取消即使它在运行也会被取消。
- tasks:Callable类型的集合
- timeout:超时时间
- unit:时间单位
异常处理:
- 如果Callable集合中只有部分Callable异常,则即使是将该异常抛出,在其调用的地方也是无法捕获异常的,因为该Callable异常了,则会调用Callable集合中的下一个Callable。
- 如果Callable集合中的Callable全部异常,则可以在其调用的地方捕获异常的。
<T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException;
<T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
3、invokeAll方法
该方法会阻塞。批量提交任务并获得他们的future,Task列表与Future列表一一对应
- tasks:Callable类型的集合
- timeout:超时时间
- unit:时间单位
异常处理
- 当线程池中线程发送发生异常时,直接在抛出,可以在其调用的方法捕获异常,但是只有在调用Future中的get才能捕获异常,否则则一样捕获不到。
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException;
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException;
4、awaitTermination方法
是阻塞方法。等待一定时间后如果有任务未结束则强行关闭
boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException;
5、isShutdown方法
判断该Executor是否关闭,调用shutdown后会变为true,即使Executor还没有关闭。
boolean isShutdown();
6、shutdown方法
是非阻塞方法。不再接受新的任务,等待所有任务执行完毕后关闭Excutor
void shutdown();
7、shutdownNow方法
- 它会对正在执行的线程进行interrupt,也有可能存在interrupt不成功的现象,如该线程中没有sleep、wait、join等即没有抛出中断异常的方法,则可能会出现中断失败
- 将空闲线程取消
- 会返回未执行的任务列表
List<Runnable> shutdownNow()
8、isTerminated方法
是非阻塞方法。判断是否Executor是否已终结,调用shutdown后该方法并不会立即返回true,只有等到所有任务都结束,Executor真正结束时才返回true
boolean isTerminated()
四、ExecutorService练习
Executors工具类这里只要理解为快建创建线程池的工具类就可以了,Executors的使用详情请看后面的博客
1、如何捕获线程执行过程中出现的异常并处理
1.1 利用Thread中uncaughtExceptionHandler来处理
- 弊端:捕获异常时无法得知实体任务中的属性。
package com.test.part3.threadpool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;
public class ExecutorServiceExample {
public static void main(String[] args) {
//创建一个core和最大线程数都为5,空闲时间为0的线程池,并且指定一个我们自己的ThreadFactory
ExecutorService executorService = Executors.newFixedThreadPool(5,new ThreadFactory());
IntStream.rangeClosed(1,5).boxed().forEach(i->{
executorService.execute(()->{
System.out.println(1/0);
});
});
}
private static class ThreadFactory implements java.util.concurrent.ThreadFactory {
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
//对线程中的异常进行捕获,但这种情况并不能得知线程任务中的属性
t.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println(t.getName()+": 发生了异常");
e.printStackTrace();
}
});
return t;
}
}
}
运行结果
Thread-0: 发生了异常
Thread-2: 发生了异常
Thread-1: 发生了异常
Thread-3: 发生了异常
Thread-4: 发生了异常
java.lang.ArithmeticException: / by zero
at com.test.part3.threadpool.ExecutorServiceExample.lambda$null$0(ExecutorServiceExample.java:15)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
java.lang.ArithmeticException: / by zero
at com.test.part3.threadpool.ExecutorServiceExample.lambda$null$0(ExecutorServiceExample.java:15)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
java.lang.ArithmeticException: / by zero
at com.test.part3.threadpool.ExecutorServiceExample.lambda$null$0(ExecutorServiceExample.java:15)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
java.lang.ArithmeticException: / by zero
at com.test.part3.threadpool.ExecutorServiceExample.lambda$null$0(ExecutorServiceExample.java:15)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
java.lang.ArithmeticException: / by zero
at com.test.part3.threadpool.ExecutorServiceExample.lambda$null$0(ExecutorServiceExample.java:15)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
1.2 定义一个抽象的实现了Runnable接口的类来处理
package com.test.part3.threadpool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;
public class ExecutorServiceExample {
public static void main(String[] args) {
//创建一个core和最大线程数都为5的线程池,且该线程池的空闲时间为0
ExecutorService executorService = Executors.newFixedThreadPool(5);
IntStream.rangeClosed(1,5).boxed().forEach(i->{
executorService.execute(new MyTask(i) {
@Override
protected void doError(Throwable cause) {
System.out.println(Thread.currentThread().getName()+": 发生了异常, 编号: "+super.no);
cause.printStackTrace();
}
});
});
}
private abstract static class MyTask implements Runnable{
private int no;
public MyTask(int no) {
this.no = no;
}
@Override
public void run() {
try {
//模拟做事情
TimeUnit.SECONDS.sleep(1);
System.out.println("编号:"+no+" 准备开始工作");
//模拟做到一半时部分出现了异常
if(no%3==0){
int num = 1/0;
}
System.out.println("编号:"+no+" 完成工作");
} catch (Exception e) {
doError(e);
}
}
//交由外部实现
protected abstract void doError(Throwable cause);
}
}
输出结果:
编号:2 准备开始工作
编号:1 准备开始工作
编号:3 准备开始工作
编号:1 完成工作
编号:2 完成工作
pool-1-thread-3: 发生了异常, 编号: 3
编号:4 准备开始工作
编号:4 完成工作
编号:5 准备开始工作
编号:5 完成工作
java.lang.ArithmeticException: / by zero
at com.test.part3.threadpool.ExecutorServiceExample$MyTask.run(ExecutorServiceExample.java:44)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)