什么线程池
线程池是用来处理线程并发的一个功能;他主要解决线程任务过多时,之前无休止的创建和销毁线程,造成线程数目不可控和系统卡顿等性能问题。线程池能控制线程的数目,同时复用空闲线程,执行未处理的线程任务;控制了最大线程数目,减少的线程创建销毁的开销;
线程池体系
java主要使用ThreadPoolExecutor,
线程池参数配置
public static void main(String[] args) {
//核心线程数目
int corePollSize = 3;
//最大线程数
int maximumPoolSize = 6;
//额外线程的保存时间
long keepAliveTime = 60L;
TimeUnit unit = TimeUnit.MILLISECONDS;
//阻塞队列
BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>();
//线程工厂
ThreadFactory threadFactory = Executors.defaultThreadFactory();
//拒绝策略
RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
corePollSize,
maximumPoolSize,
keepAliveTime,
unit,
workQueue,
threadFactory,
handler);
threadPoolExecutor.submit(new Runnable() {
@Override
public void run() {
System.out.println("task one");
}
});
}
配置线程池的几个重要参数
- corePollSize:表示核心线程数量
- maximumPoolSize:表示最大线程池数目,在阻塞队列满了之后,最大还可以创建(maximumPoolSize - corePollSize)个额外的线程
- keepAliveTime:额外线程在执行完任务后保留的时间
- unite:时间单元,与keepAliveTime关联使用
- workQueue:阻塞队列
- threadFactory :线程创建的工厂类
- handler :拒绝策略,如果新的任务无法加入到阻塞队列,且无法再创建额外线程了,就执行拒绝策略
线程池源码分析
开始一个新的线程任务,可以通过execute,也可以submit,建议使用submit,它会把Runable包装到RunnableFuture中,然后再执行execute;
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture<Void> ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}
RunnableFuture相对了Runable多实现了取消任务的接口
开始execute
这里的ctl时一个AtomicInteger对象,一个原子操作的 int值,他存在多个状态信息,后29为用来记录配置的最大核心线程数;通过workerCountOf获取当前线程池创建的多少核心线程数;
- 如果当前核心线程数没有操作配置的最大核心线程数,那么就会通过addWorker创建新的核心线程执行线程任务,然后return
- 如果不满足上步,说明当前核心线程已经都跑起来了,通过isRunning判断线程池是否没有停止,没有停止,把线程任务添加到阻塞队列里面。这种情况就是有超过核心线程数的任务线程在跑,但是阻塞队列还没满
- 当阻塞队列已经满了之后,workQueue满了之后,就会通过addWorker创建非核心线程,addWorker(command,fasle),注意有两个参数,第一个表示当前的线程任务,第二个表示是否是核心线程
addWorker是如何创建线程的
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
......
for (;;) {
int wc = workerCountOf(c);
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))//(1)
return false;
......
}
}
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
w = new Worker(firstTask);//(2)
final Thread t = w.thread;
if (t != null) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
......
workers.add(w);//(3)
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
......
} finally {
mainLock.unlock();
}
if (workerAdded) {
t.start();//(4)
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
addWorker做了这么几件事
- 判断当前是否还需要创建线程,如果addWorker第二个参数是true,那么判断核心线程是否已经到达最大了,如果是false,查看最大线程是否是最大
- 实例化Worker,每个Worke创建都会new一个Thread,然后start启动它
- 把worker条件到列表里面
Worker实例化
在Works的线程执行run之后,会直接走到runWorker这个核心代码这里
第二个红点处就是获取搭配task直接运行
第一个红点“while (task != null || (task = getTask()) != null) ”,来从阻塞队列里面获取线程任务,最后会通过poll和take方法
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
有两个作用
- 核心线程不会结束:在没有新的任务的时候会一直阻塞在这里,等待新的任务添加到阻塞队列
- 线程任务复用线程:在执行完一个线程任务之后,线程不会结束,而是继续从阻塞队列中取任务,
非核心线程可以设置时间keepAliveTime,让非核心线程执行任务之后不会立刻死掉,而是在keepAliveTime时间后没有任务才会死掉
核心线程设置allowCoreThreadTimeOut(同时必须设置keepAliveTime),让核心线程在执行完之后,如果没任务在keepAliveTime时间后死掉;这个时候核心线程和非核心线程停止的条件一致