一.ThreadPoolExecutor的参数介绍
// corePoolSize:核心线程数
// maximumPoolSize:最大线程数
// keepAliveTime:空闲线程存活时长
// unit:时间单位
// workQueue:任务队列
// threadFactory:线程工厂
// handler:拒绝策略
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
常用的阻塞队列BlockingQueue
1. ArrayBlockingQueue
- ArrayBlockingQueue 是一个用数组实现的有界阻塞队列;
- 队列满时插入操作被阻塞,队列空时,移除操作被阻塞;
- 按照先进先出(FIFO)原则对元素进行排序;
- 默认不保证线程公平的访问队列;
- 公平访问队列:按照阻塞的先后顺序访问队列,公平性会降低吞吐量。
2. LinkedBlockingQueue
- 一个基于链表结构的阻塞队列;
- 此队列按 FIFO 排序元素,吞吐量通常要高于ArrayBlockingQueue;
- 静态工厂方法 Executors.newFixedThreadPool()使用了这个队列;
- LinkedBlockingQueue具有单链表和有界阻塞队列的功能;
- 队列满时插入操作被阻塞,队列空时,移除操作被阻塞;
- 默认和最大长度为Integer.MAX_VALUE,相当于无界 (值非常大:2^31-1)。
3. SynchronousQueue
- 一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue;
- 静态工厂方法 Executors.newCachedThreadPool()使用这个队列。
- SynchronousQueue本身不存储数据,调用了put方法后,队列里面也是空的;
- 每一个put操作必须等待一个take操作完成,否则不能添加元素。
4. PriorityBlockQueue
- 一个具有优先级的无限阻塞队列。
已实现的拒绝策略
- AbortPolicy:默认策略;新任务提交时直接抛出异常RejectedExecutionException。
- CallerRunsPolicy:使用调用者所在线程运行新的任务。
- DiscardPolicy:丢弃新的任务,且不抛出异常。
- DiscardOldestPolicy:调用poll方法丢弃工作队列队头的任务,然后尝试提交新任务
如果上面的拒绝策略不能满足你的需求,也可以自定义拒绝策略,只需要实现RejectedExecutionHandler 接口即可。
二.线程池的工作流程
下面的流程图即为整个线程池的工作流程
代码证明
自定义的线程工厂
static class MyTreadFactory implements ThreadFactory {
private final AtomicInteger threadNum = new AtomicInteger(1);
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r, "my-thread-" + threadNum.getAndIncrement());
System.out.println(thread.getName() + " has been created");
return thread;
}
}
自定义的拒绝策略
static class MyRejectedPolicy implements RejectedExecutionHandler {
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
MyThread myThread = (MyThread)r;
int num = myThread.getNum();
System.err.println( "任务"+ num + "拒绝");
}
}
自定义的线程
static class MyThread implements Runnable {
private final int num;
public MyThread(int num) {
this.num = num;
}
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName() + "--开始执行" + num + "任务");
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + "--执行结束"+ num + "任务");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public int getNum() {
return num;
}
}
测试场景一:任务数 <= 核心线程数 + 队列容量
public static void main(String[] args) {
// 核心线程为 3 个
// 最大线程数为 7 个
// 队列容量为 7 个
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
3, 7, 1, TimeUnit.MINUTES,
new ArrayBlockingQueue<>(7), new MyTreadFactory(), new MyRejectedPolicy());
// 提交 10 个任务
for (int i = 0; i < 10; i++) {
MyThread myThread = new MyThread(i);
threadPoolExecutor.execute(myThread);
}
}
执行结果:
my-thread-1 has been created
my-thread-2 has been created
my-thread-3 has been created
my-thread-2--开始执行1任务
my-thread-1--开始执行0任务
my-thread-3--开始执行2任务
my-thread-3--执行结束2任务
my-thread-3--开始执行3任务
my-thread-1--执行结束0任务
my-thread-1--开始执行4任务
my-thread-2--执行结束1任务
my-thread-2--开始执行5任务
my-thread-1--执行结束4任务
my-thread-2--执行结束5任务
my-thread-1--开始执行6任务
my-thread-2--开始执行7任务
my-thread-3--执行结束3任务
my-thread-3--开始执行8任务
my-thread-1--执行结束6任务
my-thread-3--执行结束8任务
my-thread-1--开始执行9任务
my-thread-2--执行结束7任务
my-thread-1--执行结束9任务
结论:从上述的执行结果得出,一共 10 个任务,启动了 3 个核心线程,7 个任务是先放在队列中等待空闲线程去执行。
测试场景二:任务数 > 核心线程数 + 队列容量
public static void main(String[] args) {
// 核心线程为 3 个
// 最大线程数为 7 个
// 队列容量为 5 个
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
3, 7, 1, TimeUnit.MINUTES,
new ArrayBlockingQueue<>(5), new MyTreadFactory(), new MyRejectedPolicy());
// 提交 10 个任务
for (int i = 0; i < 10; i++) {
MyThread myThread = new MyThread(i);
threadPoolExecutor.execute(myThread);
}
}
执行结果:
my-thread-1 has been created
my-thread-2 has been created
my-thread-3 has been created
my-thread-4 has been created
my-thread-5 has been created
my-thread-2--开始执行1任务
my-thread-1--开始执行0任务
my-thread-4--开始执行8任务
my-thread-5--开始执行9任务
my-thread-3--开始执行2任务
my-thread-3--执行结束2任务
my-thread-1--执行结束0任务
my-thread-1--开始执行3任务
my-thread-4--执行结束8任务
my-thread-4--开始执行5任务
my-thread-2--执行结束1任务
my-thread-2--开始执行6任务
my-thread-5--执行结束9任务
my-thread-3--开始执行4任务
my-thread-5--开始执行7任务
my-thread-4--执行结束5任务
my-thread-1--执行结束3任务
my-thread-2--执行结束6任务
my-thread-3--执行结束4任务
my-thread-5--执行结束7任务
结论: 从上述的执行结果得出,一共 10 个任务,启动了 5 个线程,其中 3 个为核心线程,2 个为普通线程。因为队列只能装 5 个任务, 加上核心线程执行的 3 个任务,所有剩下的 2 个任务需要启动普通线程执行。
测试场景三:任务数 > 最大线程数 + 队列容量
public static void main(String[] args) {
// 核心线程为 3 个
// 最大线程数为 7 个
// 队列容量为 5 个
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
3, 7, 1, TimeUnit.MINUTES,
new ArrayBlockingQueue<>(5), new MyTreadFactory(), new MyRejectedPolicy());
// 提交 13 个任务
for (int i = 0; i < 13; i++) {
MyThread myThread = new MyThread(i);
threadPoolExecutor.execute(myThread);
}
}
执行结果:
my-thread-1 has been created
my-thread-2 has been created
my-thread-3 has been created
my-thread-4 has been created
my-thread-5 has been created
my-thread-6 has been created
my-thread-7 has been created
my-thread-2--开始执行1任务
my-thread-7--开始执行11任务
my-thread-3--开始执行2任务
my-thread-5--开始执行9任务
my-thread-1--开始执行0任务
my-thread-4--开始执行8任务
my-thread-6--开始执行10任务
任务12拒绝
my-thread-1--执行结束0任务
my-thread-3--执行结束2任务
my-thread-3--开始执行3任务
my-thread-6--执行结束10任务
my-thread-5--执行结束9任务
my-thread-5--开始执行6任务
my-thread-2--执行结束1任务
my-thread-2--开始执行7任务
my-thread-4--执行结束8任务
my-thread-7--执行结束11任务
my-thread-6--开始执行5任务
my-thread-1--开始执行4任务
my-thread-2--执行结束7任务
my-thread-3--执行结束3任务
my-thread-5--执行结束6任务
my-thread-6--执行结束5任务
my-thread-1--执行结束4任务
结论:从上述的执行结果得出,一共 13个任务,启动了 7 个线程,其中 3 个为核心线程,4 个为普通线程。因为队列只能装 5 个任务, 最大线程数为 7 ,最大只能接受 12 个任务,所以最后 1 个任务被拒绝了。