ThreadPoolExecutor线程池是池化思想的技术应用,除此之外还有数据库连接池等等。
目录
创建线程池的方式:
- 使用Executors创建线程池
ExecutorService es = Executors.newFixedTreadPool(2);
es.submit(Task1);//Task1是Runnable接口或者Callable接口的实现类的实例
- 手动调用ThreadPoolExecutor构造函数
ThreadPoolExecutor executor = new ThreadPoolExecutor(5,5,30L,TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>()));
executor.submit(Task1)//Task1是Runnable接口或者Callable接口的实现类的实例
实际上像上述的Executors.newFixedThreadPool()这样的方法内部调用的还是ThreadPoolExecutor的构造函数
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
ThreadPoolExecutor的构造函数:
/**
* 用给定的初始参数创建一个新的ThreadPoolExecutor。
*/
public ThreadPoolExecutor(int corePoolSize,//线程池的核心线程数量
int maximumPoolSize,//线程池的最大线程数
long keepAliveTime,//当线程数大于核心线程数时,多余的空闲线程存活的最长时间
TimeUnit unit,//时间单位
BlockingQueue<Runnable> workQueue,//任务队列,用来储存等待执行任务的队列
ThreadFactory threadFactory,//线程工厂,用来创建线程,一般默认即可
RejectedExecutionHandler handler//拒绝策略,当提交的任务过多而不能及时处理时,我们可以定制策略来处理任务
) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
ThreadPoolExecutor的三个重要参数:
- corePoolSize:线程池的核心线程数量
maximumPoolSize: 当队列中存放的任务达到队列容量的时候,当前可以同时运行的线程数量变为最大线程数。workQueue: 当新任务来的时候会先判断当前运行的线程数量是否达到核心线程数(corePoolSize),如果达到的话,新任务就会被存放在队列中。
ThreadPoolExecutor的其他参数:
- long keepAliveTime:当线程池中的线程数量大于 corePoolSize 的时候,如果这时没有新的任务提交,核心线程外的线程不会立即销毁,而是会等待,直到等待的时间超过了 keepAliveTime才会被回收销毁;
- TimeUnit unit:keepAliveTime 参数的时间单位。
- BlockingQueue<Runnable> workQueue:任务队列,用来储存等待执行任务的队列,当新任务来的时候会先判断当前运行的线程数量是否达到核心线程数,如果达到的话,新任务就会被存放在队列中
- ThreadFactory threadFactory:线程工厂,用来创建线程,一般默认即可
- RejectedExecutionHandler handler 拒绝策略,当提交的任务过多而不能及时处理时,我们可以定制策略来处理任务(触发拒绝策略的条件:线程池的线程数量达到maximumPoolSize,且还有新任务提交)
下面来讲一下线程池的拒绝策略:
ThreadPoolExecutor.AbortPolicy:抛出RejectedExecutionException来拒绝新任务的处理(默认的拒绝策略)。ThreadPoolExecutor.CallerRunsPolicy:调用提交该任务的线程去运行这个任务,也就是直接在调用execute方法的线程中运行(run)被拒绝的任务。所以当后续还有其他任务还要提交的时候,这个提交的速度自然就会变慢,因此会影响性能。ThreadPoolExecutor.DiscardPolicy: 不处理新任务,直接丢弃掉。ThreadPoolExecutor.DiscardOldestPolicy: 此策略将丢弃最早的未处理的任务请求。
线程池的工作过程 :
先附上一张图

1)如果当前运行的线程少于corePoolSize,则创建新线程来执行任务(注意,执行这一步骤需要获取全局锁)
2)如果运行的线程等于或多于corePoolSize,则将任务加入BlockingQueue。
3)如果无法将任务加入BlockingQueue(队列已满),则创建新的线程来处理任务(注意,执行这一步骤需要获取全局锁)。
4)如果创建新线程将使当前运行的线程超出maximumPoolSize,任务将被拒绝,并调用RejectedExecutionHandler.rejectedExecution()方法。
ThreadPoolExecutor采取上述步骤的总体设计思路,是为了在执行execute()方法时,尽可能地避免获取全局锁(那将会是一个严重的可伸缩瓶颈)。在ThreadPoolExecutor完成预热之后(当前运行的线程数大于等于corePoolSize),几乎所有的execute()方法调用都是执行步骤2,而步骤2不需要获取全局锁(摘要来自《Java并发编程的艺术》)
ThreadPoolExecutor的execute()方法源码:
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
/*
* 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.
*/
//变量c是一个包含了线程池的状态以及当前线程池中线程的数量的一个状态值
//其中高三维存储线程池状态,低29位存储线程池的线程数量
int c = ctl.get();
//如果当前线程池的线程数量小于核心线程数量(corepoolSize),直接创建线程并执行任务
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
//如果当前线程池的状态为运行状态(Running),并且加入工作队列成功就走这个分支
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();//再次获取线程池的状态
//double check,判断线程池是否仍然处于Running状态
if (! isRunning(recheck) && remove(command))//不处于Running状态,移出工作队列的任务,并拒绝该任务
reject(command);
//始终要保证线程池有线程在运行
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
//如果corePoolSize已经满,并且工作队列也已经饱满,则拒绝该任务
else if (!addWorker(command, false))
reject(command);
}
addWorker方法源码:
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
int c = ctl.get();//c是一个通过高低位表示线程池的状态以及当前线程池的线程数量的变量
int rs = runStateOf(c);//获取线程池的状态
// Check if queue empty only if necessary.
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;;) {
int wc = workerCountOf(c);//线程池的线程数量
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
if (compareAndIncrementWorkerCount(c))
break retry;
c = ctl.get(); // Re-read ctl
if (runStateOf(c) != rs)
continue retry;
// else CAS failed due to workerCount change; retry inner loop
}
}
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
w = new Worker(firstTask);//实际上execute传入的任务会被封装为Worker类
final Thread t = w.thread;
if (t != null) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();//获取全局锁
try {
// Recheck while holding lock.
// Back out on ThreadFactory failure or if
// shut down before lock acquired.
int rs = runStateOf(ctl.get());
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();//释放锁
}
if (workerAdded) {
t.start();//任务开始执行
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
本文详细解析了Java中ThreadPoolExecutor线程池的工作原理,包括其构造函数参数、线程池的三大核心参数及作用,拒绝策略的四种类型,以及线程池执行任务的具体流程。通过源码分析,揭示了线程池如何高效地处理任务。
170万+

被折叠的 条评论
为什么被折叠?



