[TOC]
声明
该系列文章只是记录本人回顾java多线程编程时候记录的笔记。文中所用语言并非严谨的专业术语(太严谨的术语其实本人也不会……)。难免有理解偏差的地方,欢迎指正。
另外,大神请绕路。不喜勿喷。
毕竟好记性不如烂笔头嘛,而且许多东西只要不是你经常用的最终都会一丢丢一丢丢地给忘记。
1 线程池简介
1.2 什么是线程池?
通俗点讲,线程池就是一个装有一大堆线程的容器。
这就和我们通常使用的JDBC的各种数据源有点类似了。数据源里的元素是改造过可重复利用
的数据库连接(Connection)的实例。而线程池里的元素就是可重复利用
的线程了。
1.2 使用线程池有什么好处?
- 降低开销: 主要是线程的创建和销毁的开销(提前创建)
- 提高响应速度: 省去了线程的创建和销毁的阶段,能立即响应业务处理
- 更好的管理线程: 线程池是线程的容器, 线程放在容器里更方便统一监控和管理
以下是《java并发编程的艺术》一书中对线程池工作流程的流程图
2 ThreadPoolExecutor
2.1 相关属性
ThreadPoolExecutor executor = new ThreadPoolExecutor(
5, 10, 60, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(100)
);
- corePoolSize: 线程池基本大小
- 将一个待执行的任务放到线程池中的时候,如果待执行的任务数小于该参数,线程池就会创建一个线程来执行该任务。反之,不再创建新的线程。
- 在线程池刚刚创建后,线程并不是立即启动的。如果事先调用了
prestartCoreThread()
或者prestartAllCoreThreads()
方法则会立即启动核心线程。
- poolSize: 当前大小(实际线程数)
- maximumPoolSize: 线程池中所能允许的最大线程数
- 如果队列满了,并且poolSize < maximumPoolSize,在新任务到来后会创建新的线程来执行。
- 如果底层使用的是无界队列,该参数将无意义
- allowCoreThreadTimeOut: 是否允许核心线程超时退出
- 在没有任务执行的时候,是否允许核心线程超时退出
-
false
: 表示即使是空闲时,核心核心线程也不会退出
- keepAliveTime: 是否允许线程超时退出
- 此处指的是超出核心线程数的那部分线程(corePoolSize < poolSize < maximumPoolSize)
- 而核心线程是否超时退出由
allowCoreThreadTimeOut
控制
- workQueue | runnableTaskQueue : 用来保存待执行任务的阻塞队列
- ArrayBlockingQueue
- LinkedBlockingQueue
- SynchronousQueue
- PriorityBlockingQueue
- 对于这几个队列不熟悉的可以参考 : http://blog.csdn.net/hylexus/article/details/53451307
- threadFactory: 创建线程的工厂
- RejectedExecutionHandler: 饱和处理策略,见下文
2.2 饱和处理策略
如果使用的是有界队列,很可能出现队列和线程池饱和的情况,RejectedExecutionHandler
就是用来指定如何处理这种情况的。
内置的四种实现如下:
- AbortPolicy: 抛出异常
- CallerRunsPolicy: 在调用
execute()
方法的线程里执行饱和的任务 - DiscardOldestPolicy: discards the oldest unhandled request
- 此处不知道怎么翻译了,
oldest
可以理解为最靠近队首
的元素 - 或者说是
最近
的
- 此处不知道怎么翻译了,
- DiscardPolicy: 直接丢弃
2.3 提交/执行任务
线程池创建好之后,下一步就是向线程池提交任务让线程池中的线程去执行。
提交的任务必须是Callable
或者Runnable
的实现类。
提交任务,有如下方法:
public void execute(Runnable command){}
public Future<?> submit(Runnable task){}
public <T> Future<T> submit(Callable<T> task){}
public <T> Future<T> submit(Runnable task, T result){}
其中,execute()
方法用于提交没有返回值的任务。所以无法得知提交的任务什么时候被执行,也无法知道何时执行完毕了。
this.executor.execute(() -> {
// task to executed
});
submit()方法用于提交带有返回值的任务。
Future<String> future = this.executor.submit(new Callable<String>() {
@Override
public String call() throws Exception {
return null;
}
});
try {
String result = future.get();
System.out.println(result);
} catch (InterruptedException e) {
// 中断
e.printStackTrace();
} catch (ExecutionException e) {
// 任务执行错误
e.printStackTrace();
} finally {
this.executor.shutdown();
}
2.4 停止线程池
提供了两个方法来终止线程池: shutdown()
和shutdownNow()
这两个方法都是通过遍历线程,逐个调用线程的interrupt()方法来终止线程的。
所以问题来了:
无法响应中断的线程,可能就没法终止了
另外,在shutdown()
方法中,还调用了onShutdown
方法。这个方法方法体的实现是空的。
可以重写这个hook方法来在线程池销毁的时候完成一些其他的事情。
参考资料
- 《java并发编程的艺术》