线程池参数
- corePoolSize:核心线程池数量
- maximumPoolSize:最大线程池数量
- keepAliveTime:非核心线程的空闲状态的存活时间
- util:单位时间(天,小时,秒)
- workqueue:工作队列(阻塞队列)
- threadFactory:线程工厂(作用:创建线程)
- handler:拒绝策略
- ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常
- ThreadPoolExecutor.DiscardPolicy:丢弃任务但不抛出异常
- ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务
- ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务
线程池运行原理
注: 创建:先创建核心线程,再放到阻塞队列,再创建非核心线程 执行:先用核心线程执行,再用非核心执行,最后阻塞队列(执行过程是非公平的)
示例
public class CustomThreadPool {
public static void main(String[] args) {
ExecutorService threadPool = new ThreadPoolExecutor(
10,
20,
0l,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(10),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy()
);
class MyTask implements Runnable {
int i = 0;
public MyTask(int i) {
this.i = i;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "程序员做第" + i + "个项目");
try {
Thread.sleep(3000l);
} catch (Exception e) {
e.printStackTrace();
}
}
}
try {
for (int j = 0; j < 100; j++){
MyTask task = new MyTask(j);
threadPool.execute(task);
}
}catch (Exception e){
e.printStackTrace();
}finally {
threadPool.shutdown();
}
}
}
从打印可以看出:
创建:先创建核心线程,再放到阻塞队列,再创建非核心线程
执行:先用核心线程执行,再用非核心执行,最后阻塞队列(执行过程是非公平的)`
源码分析
1
public class ThreadPoolTest {
public static void main(String[] args) {
//构建一个线程池,此时没有创建线程
ExecutorService executor = new ThreadPoolExecutor(
10,
200,
0l,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(10),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy()
);
//提交一个任务到线程池
executor.execute(new Runnable() {
@Override
public void run() {
System.out.println("zhangsan...");
}
});
}
}
1.跟进execute方法:
2.addWorker()创建一个线程,然后使用
start()`启动线程
3.假设提交4个任务,第一个任务执行完了,是新建一个线程执行,还是使用第一个任务执行完的线程执行呢?
- 从源码可以看出
(workerCountOf(c):计算当前线程数)
,如果当前线程数小于核心,则新建线程执行:
4.如果线程1执行完了,会销毁还是存活?
- 伪代码
会到阻塞队列获取任务,如果队列为空,则阻塞。
5.新提交的第11任务,调offer()
放到阻塞队列
然后核心线程取获取阻塞队列任务执行。注:并不是直接丢给核心线程去执行,主要是为了线程执行完任务利用阻塞队列保活,这也是设计的奇妙之处。
6.阻塞队列满后,addWorker()
新创建线程执行
队列是满的,新建线程却执行新的任务,而不是队列任务,可见线程池是非公平的。
7.阻塞队列满且最大线程数达到,则掉用reject()拒绝
8.四种拒绝策略
AbortPolicy
默认拒绝策略,抛出异常。
CallerRunsPolicy
调用方执行。
DiscardOldestPolicy
取出队列最前面一个任务,出队列,然后执行当前任务。
DiscardPlicy
空方法,啥也不做
9.线程池淘汰策略?
正常执行完
task.run();
:执行真正调用者的run();
getTask()
:从阻塞队列里面获取任务
runWorker()
:线程池运行提交的一个任务
大于核心数:
如果阻塞队列没有任务,compareAndDecrementWorkerCount()
:cas淘汰一个线程
有任务,workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS)
,
小于等于核心数:
如果阻塞队列没有任务, workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) 则返回继续执行。否则核心线程阻塞,workQueue.take()
;等待任务提交
异常退出
线程执行异常退出,抛出异常。
抛出异常后,会调用processWorkerExit(w, completedAbruptly)
重新创建一个线程。
线程池关闭
shutdown():执行完阻塞队列线程和正在运行的线程,再关闭。
showdownNow():执行完正在运行的线程,就关闭,不执行阻塞队列任务。
- 线程两种情况:
- 第一种,正在运行任务
第一步调用 shutdown():advanceRunState(SHOTDOWN),
调用 shutdownNow():advanceRunState(STOP),
设置正在执行任务线程状态。
第二步:发送一个中断信号,interruptIdleWorkers(),是否执行终端,由执行任务的方法决定。 - 第二种 take()方法阻塞任务:
阻塞队列take()根据中断信号,中断阻塞队列线程
- 第一种,正在运行任务
![!](https://i-blog.csdnimg.cn/direct/42fa303461ed4feaa6409b475add8be9.png)
扩展 异常推出回调机制:uncaughtException(Thread t,Throwable e);
线程异常执行推出,会回调uncaughtException()
方法。