参考了 Matrix海 子的博客 Java并发编程:线程池的使用
ThreadPoolExecutor的构造函数
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
- corePoolSize:核心池的大小
- maximumPoolSize:线程池最大线程数
- keepAliveTime:表示线程没有任务执行时最多保持多久时间会终止
- unit:参数keepAliveTime的时间单位
- workQueue:任务缓存队列及排队策略:
- threadFactory:线程工厂,用于创建线程,一般用默认即可;
- handler:任务拒绝策略: 表示当拒绝处理任务时的策略。
任务缓存队列及排队策略
一个阻塞队列,阻塞队列有以下几种
- ArrayBlockingQueue;
有界任务队列,若有新的任务需要执行时,线程池会创建新的线程,直到创建的线程数量达到corePoolSize时,则会将新的任务加入到等待队列中。若等待队列已满,即超过ArrayBlockingQueue初始化的容量,则继续创建线程,直到线程数量达到maximumPoolSize设置的最大线程数量,若大于maximumPoolSize,则执行拒绝策略。 - LinkedBlockingQueue;
基于链表的先进先出队列,如果创建时没有指定此队列大小,则默认为Integer.MAX_VALUE; - SynchronousQueue; 这个队列比较特殊,它不会保存提交的任务,而是将直接新建一个线程来执行新来的任务。
任务拒绝策略
当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize,如果还有任务到来就会采取任务拒绝策略,通常有以下四种策略:
- ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。这种策略是默认的拒绝策略
- ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
- ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
- ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务
Executors提供的三个静态方法来创建线程池
Executors.newFixedThreadPool(int)(固定大小线程池
Executors.newSingleThreadExecutor()(单个后台线程)
Executors.newCachedThreadPool()(无界线程池,可以进行自动线程回收)
- newFixedThreadPool 固定大小的线程池 创建的线程池corePoolSize和maximumPoolSize值是相等的,它使用的LinkedBlockingQueue;
- newSingleThreadExecutor 单线程的线程池 将corePoolSize和maximumPoolSize都设置为1,也使用的LinkedBlockingQueue;
- newCachedThreadPool 有缓冲池的线程池 将corePoolSize设置为0,将maximumPoolSize设置为Integer.MAX_VALUE,使用的SynchronousQueue,也就是说来了任务就创建线程运行,当线程空闲超过60秒,就销毁线程。
线程池执行过程
- 如果当前线程池中的线程数目小于corePoolSize,则每来一个任务,就会创建一个线程去执行这个任务;
- 如果当前线程池中的线程数目>=corePoolSize,则每来一个任务,会尝试将其添加到任务缓存队列当中,若添加成功,则该任务会等待空闲线程将其取出去执行;若添加失败(一般来说是任务缓存队列已满),则会尝试创建新的线程去执行这个任务;
- 如果当前线程池中的线程数目达到maximumPoolSize,则会采取任务拒绝策略进行处理;
- 如果线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止,直至线程池中的线程数目不大于corePoolSize;如果允许为核心池中的线程设置存活时间,那么核心池中的线程空闲时间超过keepAliveTime,线程也会被终止。
demo
新建 MyRejected
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
public class MyRejected implements RejectedExecutionHandler {
public MyRejected(){
}
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
System.out.println("当前被拒绝任务为:" + r.toString());
}
}
新建 ThreadExecutor
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ThreadExecutor {
public static void main(String[] args) throws InterruptedException {
ThreadExecutor threadExecutor = new ThreadExecutor();
threadExecutor.test01();
}
/**
* 创建线程池
*/
public void test01() throws InterruptedException {
RejectedExecutionHandler handler = new MyRejected();
ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 200, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(5), handler);
for(int i=0; i<15; i++){
// for(int i=0; i<20; i++){
MyTask myTask = new MyTask(i);
executor.execute(myTask);
Thread.sleep(10);
}
executor.shutdown();
}
}
class MyTask implements Runnable {
private int taskNum;
public MyTask(int num) {
this.taskNum = num;
}
@Override
public void run() {
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("task "+taskNum+"执行完毕");
}
}
先执行了 0 -4 的线程,5 - 9 的线程放入了ArrayBlockingQueue队列中,紧接着10 - 14 的线程准开始执行,发现队列放不下了,因此去创建线程去执行,0 - 4 线程被执行完了之后,线程池取出队列中的线程去执行。
当 for 循环,循环创建20个线程时,执行了拒绝策略
根据此截图看出超出了最大线程数时,同时队列也满了,线程池再执行线程则会执行线程池的拒绝策略,因此在这个demo中,MyRejected抛出了异常
提前创建线程
创建线程池后,当执行任务时,才会创建线程,如果创建线程池的时候,就调用了prestartAllCoreThreads,则会先创建线程,等待任务使用
- public int prestartAllCoreThreads()启动所有核心线程,使其处于等待工作的空闲状态。仅当执行新任务时,此操作才重写默认的启动核心线程策略。