频繁地创建和销毁线程,会浪费资源。因此产生了线程池,缓存并重用线程,提高性能。
使用java线程池,最简单的方式是Executors和ExecutorService。
(图片来源: https://www.logicbig.com/tutorials/core-java-tutorial/java-multi-threading/thread-pools.html
)
ExecutorService提供了线程、线程池相关管理的抽象,包括提交任务(submit、execute)、关闭线程池。
Executors是一个工具类,提供了创建线程池的入口。
ExecutorService es = Executors.newCachedThreadPool();
常见创建线程池
java8以后新增newWorkStealingPool,创建工作窃取类型的线程池,以后再专门讨论working steal,这里先不深入展开。
真实创建线程池,依靠ThreadPoolExecutor。Executors只是简单的封装。
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
ThreadPoolExecutor的构造函数,有一堆定制参数。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
}
}
参数意义如下:
当缓存任务数量大于workQueue容量,且工作线程数到达maxPoolSize,会触发DiscardPolicy处理,有4种处理策略
从ThreadPoolExecutor构造函数可以看到多个控制参数,最为核心的是线程池大小、阻塞队列、拒绝策略。
举个例子,打开Executors,查看newFixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
那么当线程数到达corePoolSize,新提交的任务将会堆积到workQueue,由于类型是LinkedBlockingQueue,没有做长度限制,就会有可能堆积大量的任务,消耗大量内存,引发频繁gc、甚至OOM问题。
如果手动使用ThreadPoolExecutor创建线程池,那么新建线程池的时候就要考虑清楚适合业务场景的线程池配置,可控性更高。这也是阿里推荐使用ThreadPoolExecutor创建线程池的原因。
接下来深入了解ThreadPoolExecutor的代码。
调用的是execute()。
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
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.
*/
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);
}
addWorker()是底层实现添加任务的类。核心是在循环内进行CAS检查,避免使用锁。
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
int c = ctl.get();
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);
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;
}
阅读ThreadPoolExecutor源码,需要留意ctl变量。
ctl变量把workerCount和runState打包到一个int类型。很巧妙的做法:
In order to pack them into one int, we limit workerCount to * (2^29)-1 (about 500 million) threads rather than (2^31)-1 (2 * billion) otherwise representable.
定义ctl变量
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)); // 32 - 3 = 29 private static final int COUNT_BITS = Integer.SIZE - 3; private static final int CAPACITY = (1 << COUNT_BITS) - 1;
把运行状态保存在高位,其余位数表示线程数。注意running状态,ctl是负数。
// runState is stored in the high-order bits 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;
接下来就容易理解了。
// Packing and unpacking ctl
private static int runStateOf(int c) { return c & ~CAPACITY; }
// 和CAPACITY按位与,丢弃了高位(状态位)
private static int workerCountOf(int c) { return c & CAPACITY; }
private static int ctlOf(int rs, int wc) { return rs | wc; }