线程池
前言
线程池因为可以线程复用而在工作使用频繁,由此需掌握线程池的基本参数和大致工作流程
一、线程池参数
创建线程池时,需要指定核心线程数,最大线程数,空闲时间,时间类型,线程工厂,拒绝策略,任务队列
其中核心线程数和最大线程数的设定规则有两种,IO密集型还是cpu密集型
查mysql或者file处理都是IO密集型
对于cpu密集型,线程数最好等于cpu核心数,我们可以通过一下api获取
Runtime.getRuntime().availableProcessors()
为了以防万一,我们往往会多加一个线程最为替补,所以线程数最佳为cpu核心数+1
IO型由于大部分执行时间可能在IO的处理上,cpu并没有一直在运行,从而导致资源浪费,所以通常线程数最佳为2*cpu核心数。但是可能2倍的cpu核心数也不够执行,则可以用以下公式
线程数=cpu核心数*(1+线程等待时间/运行总时间)
可以用jvisualvm.exe(jdk的bin目录下有)来估计
线程池指定了核心线程的数量,但一开始线程池中不会有线程,而是随着任务的提交创建线程
线程池有五个状态运行中,关闭,停止,池空,终结
* RUNNING: Accept new tasks and process queued tasks
* SHUTDOWN: Don't accept new tasks, but process queued tasks
* STOP: Don't accept new tasks, don't process queued tasks,
* and interrupt in-progress tasks
* TIDYING: All tasks have terminated, workerCount is zero,
* the thread transitioning to state TIDYING
* will run the terminated() hook method
* TERMINATED: terminated() has completed
private static final int RUNNING = -1 << COUNT_BITS;
private static final int SHUTDOWN = 0 << COUNT_BITS;
private static final int STOP = 1 << COUNT_BITS;
private static final int TIDYING = 2 << COUNT_BITS;
private static final int TERMINATED = 3 << COUNT_BITS;
二、线程池工作流程
接下来根据几个问题来解释线程池工作的部分场景
1.当线程池中的线程有空闲时,新的任务加进来会创建新的线程,还是利用旧的线程?
这个问题需要分情况,观察源码execute方法可发现,第2个if中判断若线程池中已有的线程数小于指定的核心线程数,则无论是否有空闲线程,依旧创建新的线程。
第3个if中判断若当前线程数大于等于核心线程数,则提交的任务offer进入工作等待队列BlockingQueue
第4个if中判断当等待队列已满,加入不成功时,调用addWorker方法。这个方法会判断当前线程数是否大于设置的最大线程maximumPoolSize数,若小于则创建一个新线程去执行任务。由此可见线程池的任务是非公平的,可能后发起的任务先执行
public void execute(Runnable command) {
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);
}
2.当前线程池线程个数大于核心线程数时,空闲的线程是否会销毁?
答案是会
这里需要看源码,在执行run,也就是runWorker方法时,它调用了getTask方法。可以看到最开始,会调用poll方法阻塞设定的空闲时间,若指定时间内无新任务提交,则timedOut = true,从而调用compareAndDecrementWorkerCount方法,通过cas让当前线程数-1,并返回null 销毁当前的线程
private Runnable getTask() {
boolean timedOut = false; // Did the last poll() time out?
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
int wc = workerCountOf(c);
// Are workers subject to culling?
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}