线程池技术产生背景
在应用服务器端,需要大量处理来自用户的任务,如果每接收到一个任务服务器端采取采用一个新的线程来处理,然后等到线程结束又关闭,这会造成大量的上下文切换,并且线程的创建和销毁带来的资源占用也很巨大,这就造成了资源浪费。我们通过采用线程池的技术,预先创建若干数量的线程,并且对这些线程的管理不交给用户,因此可以不断地重复利用创建好了的线程去执行任务,从而避免了线程创建和销毁的资源损耗,并减少了上下文切换,因为线程数减少了嘛。
线程池带来的好处
- 降低资源损耗
- 提高响应速度
- 提高线程的可管理性
线程池的实现原理
线程池的处理流程:
(1)线程池判断核心线程池里面是否所有线程都处于工作状态,如果不是就创建一个新的线程执行任务
(2)线程池判断工作队列是否满了,没满就将任务放入工作队列中
(3)线程池判断线程池里面是否所有线程都处于工作状态,如果不是则创建一个新的线程执行任务
(4)无法执行任务
线程池执行任务的方法源码分析(JDK8里面的源码):
public void execute(Runnable command) {
/*
* Proceed in 3 steps:
*
* 1. If fewer than corePoolSize threads are running, try to
* start a new thread with the given command as its first
* task. The call to addWorker atomically checks runState and
* workerCount, and so prevents false alarms that would add
* threads when it shouldn't, by returning false.
*
* 2. If a task can be successfully queued, then we still need
* to double-check whether we should have added a thread
* (because existing ones died since last checking) or that
* the pool shut down since entry into this method. So we
* recheck state and if necessary roll back the enqueuing if
* stopped, or start a new thread if there are none.
*
* 3. If we cannot queue task, then we try to add a new
* thread. If it fails, we know we are shut down or saturated
* and so reject the task.
*/
if (command == null)
throw new NullPointerException();
int c = ctl.get();
//如果当前线程数小于核心线程数,创建线程并立即执行任务
if (workerCountOf(c) < corePoolSize) {
//自动检验线程的工作状态和工作数量
if (addWorker(command, true))
return;
//获取处于工作状态的线程数
c = ctl.get();
}
//双重检验是否应该将任务加入工作队列,因为存在第一次检查之后其他线程死亡的情况以及线程池被关闭的状况
//确保没有线程死亡
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
//新任务已经被运行了,将任务移除任务队列
if (! isRunning(recheck) && remove(command))
reject(command);
//线程池被关闭情况
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
//未达到最大线程数,再次新增一个线程进行任务处理,失败说明线程池拒绝执行超负荷任务
else if (!addWorker(command, false))
reject(command);
}
工作线程会将线程封装成工作线程Work,并在run方法里面不断获取工作队列里的任务来执行
合理配置线程池
对于CPU密集型的服务器,应该配置尽可能少的线程,如N(cpu数量)+1个线程的线程池,对于IO密集型任务,应配置尽可能多的线程,如2×N(cpu数量)线程的线程池。因为IO密集,需要更长的时间等待处理IO操作,如果线程数太少,此时CPU就会处于空闲状态,并且还有很多的任务等待执行,执行效率很低。
线程池监控
通过监控线程池需要执行的任务数量(taskCount)、以执行的任务数量(completedTaskCount)、最大线程创建情况(largestPoolSize)等数据可得到该线程池的吞吐量以及总结出最佳的配置参数。