目录结构
ExecutorService
继承树
ExecutorService是java对线程池定义的一个接口,也是java默认给我们的,它具有两个实现类:
- ThreadPoolExecutor
- ScheduledThreadPoolExecutor
ExecutorService
继承了Executor
接口,这个接口只有一个excute()
方法,这里我们展示一下它们的类图:
原理
一般在实际业务中,我们对于线程的使用都是比较频繁的,但是线程之间的切换是非常消耗cpu的,所以我们会使用线程池,也就是java提前创建好了许多线程,利用可回收的池化的概念,来完成对线程池的优化的工作。
相关参数
ThreadPoolExecutor参数
- corePoolSize:核心线程数
- maximumPoolSize:最大线程数
- keepAliveTime:线程最大空闲时间
- TimeUnit:时间单位
- workQuene:线程等待队列
- threasFactory:线程创建工厂
- RejectedExecutionHander:拒绝策略
corePoolSize、maximumPoolSize、RejectedExecutionHander
corePoolSize
、maximumPoolSize
控制了线程的创建数量,corePoolSize
定义了核心线程的数量,maximumPoolSize
定义了当前线程池的最大线程数量,RejectedExecutionHander
定义了拒绝的策略。- 三者关系:当线程的创建的数量<
corePoolSize
的时候,无论当前线程是否空闲,当有新的任务加入的时候都会新建一个线程,但是当线程池的创建数量≥corePoolSize
的时候,新的任务出现如果是有空闲线程就会重用空闲线程,如果没有空闲的线程就会新建一个线程;当线程的数量≥maximumPoolSize
的时候,多余的线程就会进入到排队中,当排队的数量过多的时候,在实际业务中我们机会执行拒绝策略。
keepAliveTime、TimeUnit
- keepAliveTime是线程空闲的时间、TimeUnit是时间的单位,这两个的组合,才可以定义一个完整的时间。
- 这个时间用来处理线程回收,超过这个时间的线程会被回收,但是默认对于核心线程是无效的,只有通过设置才可以实现对核心线程有效。
workQuene
线程等待队列,主要是用来存放提交的任务。
RejectedExecutionHander
拒绝策略,当线程池被关闭或者线程池的线程满了而且队列也满了才会启动拒绝策略。
创建ExecutorService
java给我们提供了Executors工厂类
,可以帮助我们创建各种类型的ExecutorService
线程池,如下四种:
1. newCachedThreadPool 创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
2. newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
3. newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
4. newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
这里值得注意的是,
Executors
只是一个工厂类,它所有的方法返回的都是ThreadPoolExecutor
、ScheduledThreadPoolExecutor
这两个类的实例。
使用ExecutorService
ExecutorService executorService = Executors.newFixedThreadPool(10);
executorService.execute(new Runnable() {
public void run() {
System.out.println("Asynchronous task");
}
});
executorService.shutdown();
执行ExecutorService
ExecutorService
的执行方法有四种
- execute(Runnable)
- submit(Runnable)
- submit(Callable)
- invokeAny(...)
- invokeAll(...)
execute(Runnable)
这个方法可以接受一个runnable
实例,并且异步执行,但是这个方法没有办法知道task的执行结果:
ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.execute(new Runnable() {
public void run() {
System.out.println("Asynchronous task");
}
});
executorService.shutdown();
submit(Runnable)
submit(Runnable)
也是接收一个runnable
实例,不同的是,这个方法可以返回一个Future
对象,通过返回的Future
对象,我们可以检查提交的任务是否执行完成:
ExecutorService executorService = Executors.newSingleThreadExecutor();
Future future = executorService.submit(new Runnable() {
public void run() {
System.out.println("Asynchronous task");
}
});
System.out.println(future.get());//如果这里返回一个null,表示任务执行完毕,这个方法会产生阻塞。
submit(Callable)
这个方法接收的是Callable
实例,然后也会返回一个Future
对象。除此之外,因为这个方法接收的是Callable
接口的一个实例,这个接口会有一个call()
方法的返回值,可以返回任务的执行结果。
ExecutorService executorService = Executors.newSingleThreadExecutor();
Future future = executorService.submit(new Callable(){
public Object call() throws Exception {
System.out.println("Asynchronous Callable");
return "Callable Result";
}
});
System.out.println("future.get() = " + future.get());
如果任务执行完成,future.get()方法会返回Callable任务的执行结果。注意,future.get()方法会产生阻塞。
invokeAny(Callable)
invokeAny(Callable)
接收一个Callable
集合,执行的方法不会返回一个Future
,但是会返回所有的callable
任务中其中一个任务的执行结果,但是不能保证返回的是哪一个执行的结果,反正就是其中一个。
ExecutorService executorService = Executors.newSingleThreadExecutor();
Set<Callable<String>> callables = new HashSet<Callable<String>>();
callables.add(new Callable<String>() {
public String call() throws Exception {
return "Task 1";
}
});
callables.add(new Callable<String>() {
public String call() throws Exception {
return "Task 2";
}
});
callables.add(new Callable<String>() {
public String call() throws Exception {
return "Task 3";
}
});
String result = executorService.invokeAny(callables);
System.out.println("result = " + result);
executorService.shutdown();
invokeAll(Callable)
invokeAll(Callable)
也是接收一个Callable
集合,然后返回一个Future
的list
,对应每一个任务执行后的Future
对象
ExecutorService executorService = Executors.newSingleThreadExecutor();
Set<Callable<String>> callables = new HashSet<Callable<String>>();
callables.add(new Callable<String>() {
public String call() throws Exception {
return "Task 1";
}
});
callables.add(new Callable<String>() {
public String call() throws Exception {
return "Task 2";
}
});
callables.add(new Callable<String>() {
public String call() throws Exception {
return "Task 3";
}
});
List<Future<String>> futures = executorService.invokeAll(callables);
for(Future<String> future : futures){
System.out.println("future.get = " + future.get());
}
executorService.shutdown();
关闭ExecutorService
-
当我们使用完成ExecutorService之后应该关闭它,否则它里面的线程会一直处于运行状态。
-
举个例子,如果的应用程序是通过
main()
方法启动的,在这个main()
退出之后,如果应用程序中的ExecutorService
没有关闭,这个应用将一直运行。之所以会出现这种情况,是因为ExecutorService
中运行的线程会阻止JVM
关闭。 -
如果要关闭
ExecutorService
中执行的线程,我们可以调用ExecutorService.shutdown()
方法。在调用shutdown()
方法之后,ExecutorService
不会立即关闭,但是它不再接收新的任务,直到当前所有线程执行完成才会关闭,所有在shutdown()
执行之前提交的任务都会被执行。 -
如果我们想立即关闭
ExecutorService
,我们可以调用ExecutorService.shutdownNow()
方法。这个动作将跳过所有正在执行的任务和被提交还没有执行的任务。但是它并不对正在执行的任务做任何保证,有可能它们都会停止,也有可能执行完成。
使用RunTime.getRunTime().addShutdownHook优雅关闭线程池
-
有时候我们用到的程序不一定总是在
JVM
里面驻守,可能调用完就不用了,释放资源. -
RunTime.getRunTime().addShutdownHook
的作用就是在JVM
销毁前执行的一个线程.当然这个线程依然要自己写. -
利用这个性质,如果我们之前定义了一系列的线程池供程序本身使用,那么就可以在这个最后执行的线程中把这些线程池优雅的关闭掉.
比如我们定义了一个线程池
private ExecutorService streamThreadPool = Executors.newFixedThreadPool(streamNum);
我们对它优雅的关闭
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
shutdownGracefully();
}
});
public void shutdownGracefully() {
shutdownThreadPool(streamThreadPool, "main-pool");
}
/**
* 优雅关闭线程池
* @param threadPool
* @param alias
*/
private void shutdownThreadPool(ExecutorService threadPool, String alias) {
log.info("Start to shutdown the thead pool: {}", alias);
threadPool.shutdown(); // 使新任务无法提交.
try {
// 等待未完成任务结束
if (!threadPool.awaitTermination(60, TimeUnit.SECONDS)) {
threadPool.shutdownNow(); // 取消当前执行的任务
log.warn("Interrupt the worker, which may cause some task inconsistent. Please check the biz logs.");
// 等待任务取消的响应
if (!threadPool.awaitTermination(60, TimeUnit.SECONDS))
log.error("Thread pool can't be shutdown even with interrupting worker threads, which may cause some task inconsistent. Please check the biz logs.");
}
} catch (InterruptedException ie) {
// 重新取消当前线程进行中断
threadPool.shutdownNow();
log.error("The current server thread is interrupted when it is trying to stop the worker threads. This may leave an inconcistent state. Please check the biz logs.");
// 保留中断状态
Thread.currentThread().interrupt();
}
log.info("Finally shutdown the thead pool: {}", alias);
}