线程池 的出现,主要是用来管理一组线程的工作运行状态,这样可以方便JVM更好的利用CPU资源。
Java线程池的工作原理:JVM先根据用户的参数创建一定数量的可运行的线程任务,并且将这些任务放入到队列中,在线程创建之后,启动这些任务。
如果当线程数超过最大线程数,这个线程数是由用户在创建线程池的时候进行设计的。对于超过数量的线程来说进行等候排队,等待其他线程执行完毕之后,线程池会发现可用的线程,进行再次从队列中获取到任务并执行。
线程池的主要作用是线程复用、线程资源管理、控制操作系统的最大并发数量,从而保证系统的高效运行,这个高效运行时通过线程池内部的资源重复利用来实现。并且通过控制最大并发数保证系统资源的合理利用。
线程的复用
如果我们对于 线程 进行抽象的话,那么每个 thread 类都有一个start()方法,在程序调用到start方法之后线程启动,JVM会调用这个类的run方法。之前提到过,Thread类的run方法其实调用了Runnbale的run方法。所以,继承了Thread类,在start方法中不断循环调用传入的Runnable对象,程序就不断的执行run方法中的代码。
这样的话可以将方法中的Runnbale对象,放入到一个Runnbale的队列中,当线程在获取到下一个Runnbale对象的时候可以进行阻塞,这样可以控制正在执行的线程的数量,也可以有效的保证系统中的线程等待其他正确的线程执行完毕,这样的一个机制就是线程池,可以对队列以及创建的线程资源进行重复的利用。多个Runnbale可以被一个Thread对象重复调用start方法来执行。
线程池的核心组件和核心类
Java中的线程池有如下的几个核心组件组成
- 线程池管理器:用于创建并且管理线程池
- 工作线程:线程池中正在执行的具体的任务的线程
- 任务接口:用来定义工作线程如何调度,如何执行,线程实现了这个接口,才能被线程池认可,进入线程池的管理调度。
- 任务队列:用来存放需要处理的等待任务,新任务会不断地进入到队列中进行等待,执行完成之后任务会被从队列中移除
Java中线程池机制是通过Executor框架进行实现的

在框架中用到Executor、Executors、ExecutorService、ThreadPoolExecutor、Callable、Future、FutureTask一些核心类。
其中, ThreadPoolExecutor 是用来构建线程的核心,代码如下
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
这个类的 构造方法 有如下几个参数
- corePoolSize:线程池中核心线程的数量
- maximumPoolSize:线程池中最大线程的数量
- keepAliveTime:当前线程数量超过corePoolSize的时候,空闲线程存活时间
- unit:keepAliveTime的 时间单位
- workQueue:任务队列,已经提交但未被执行的任务存放的空间
- threadFactory:线程工厂,用于创建线程。
- handler:用来处理任务过多或者其他的原因导致线程池无法处理的时候,任务的拒绝策略。
线程池的 工作流程

Java线程池工作流程:线程被创建的时候,向系统申请一个用于执行线程队列和管理线程的资源,然后调用execute()添加任务会按照下面的流程执行任务
- 1、如果正在运行的线程数量少于corePoolSize的数量,线程池就会立即创建并执行这个线程任务。
- 2、如果正在运行的线程数量大于等于corePoolSize的数量,那么这个任务就会被添加到队列中。
- 3、如果队列满了并且正在运行的线程数量少于maximumPoolSize的数量的时候,线程池就会创建非核心线程执行任务。
- 4、如果队列已经满了并且正在运行的线程数量大于或者等于maximumPoolSize的数量时,线程队列执行并且抛出RejectedExecutionHandler的异常
- 5、在线程任务执行完毕之后,线程任务将会被从线程队列中移除,线程池将会获取下一个任务继续进行。
- 6、在线程处于空闲状态,并且时间超过了keepAliveTime时间的时候,正在运行的线程数量超过corePoolSize的数量,这个线程就会被认定空闲线程并停止,所以在线程池中所有的线程任务执行完毕之后,线程池会收缩到corePoolSize的大小
拒绝策略
我们都知道,如果在线程池中核心线程被用完并且队列已经满了,这个时候线程池的资源已经耗尽了,线程池将没有足够的线程资源执行新的任务的时候,为了保证线程的安全性这个时候就会有拒绝策略的出现,也就是说禁止线程提交。
JDK 中内置了几个拒绝策略,分别是AbortPollicy、CallerRunsPolicy、DiscardOldestPolicy、DiscardPolicy。当然还可以实现自定义的拒绝策略。

AbortPolicy
直接抛出异常,阻止线程正常运行。
/**
* A handler for rejected tasks that throws a
* {@code RejectedExecution Exception }.
*/
public static class AbortPolicy implements RejectedExecutionHandler {
/**
* Creates an {@code AbortPolicy}.
*/
public AbortPolicy() { }
/**
* Always throws RejectedExecutionException.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
* @throws RejectedExecutionException always
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
}
CallerRunsPolicy
如果被丢弃的线程任务没有关闭,则执行该线程任务。需要注意的是,CallerRunsPolicy拒绝策略不会真的丢弃任务。
/**
* A handler for rejected tasks that runs the rejected task
* directly in the calling thread of the {@code execute} method,
* unless the executor has been shut down , in which case the task
* is discarded.
*/
public static class CallerRunsPolicy implements Reject edExecutionHandler {
/**
* Creates a {@code CallerRunsPolicy}.
*/
public CallerRunsPolicy() { }
/**
* Executes task r in the caller's thread, unless the executor
* has been shut down, in which case the task is discarded.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
}
DiscardOldestPolicy
移除线程队列中最早的一个线程任务,并且尝试提交当前任务。
/**
* A handler for rejected tasks that discards the oldest unhandled
* request and then retries {@code execute}, unless the executor
* is shut down, in which case the task is discarded.
*/
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code DiscardOldestPolicy} for the given executor.
*/
public DiscardOldestPolicy() { }
/**
* Obtains and ignores the next task that the executor
* would otherwise execute, if one is immediately available,
* and then retries execution of task r, unless the executor
* is shut down, in which case task r is instead discarded.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute(r);
}
}
}
DiscardPolicy
丢弃当前的线程任务而不做任何的处理。如果系统允许在资源不足的情况下丢弃部分的任务,这是保证系统安全的一种很好的解决方案。
/**
* A handler for rejected tasks that silently discards the
* rejected task.
*/
public static class DiscardPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code DiscardPolicy}.
*/
public DiscardPolicy() { }
/**
* Does nothing , which has the effect of discarding task r.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
}
自定义的拒绝策略
可以看到以上的四种策略都是继承了RejectedExecutionHandler接口,如果无法满足实际的使用需求,开发者可以通过自己实现RejectedExecutionHandler的接口来实现自定义的拒绝策略,并且定义自定义的异常来处理拒绝之后的内容。
private static class RejectHandler implements RejectedExecutionHandler {
private int discardNumber = 5;
private List<Runnable> discardList = new ArrayList<Runnable>();
private RejectHandler(int discardNumber) {
this.discardNumber = discardNumber;
}
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if(e.getQueue().size()>discardNumber){
// 批量移除线程队列中的指定数量的线程
e.getQueue().drainTo(discardList,discardNumber);
discardList.clear();// 清空discardList列表
if(!e.isShutdown()){
e.execute(r) // 尝试提交当前任务
}
}
}
}