ThreadPoolExecutor,简单来说就是一个线程池的Java实现,通过不同的参数可以达到不同的线程维护策略,在构造函数里,提供以下参数:
1. corePoolSize。核心线程数。永久保留在线程池的线程数,即使处于空闲也不会被回收。
2. maximumPoolSize。最大线程数。线程池并发线程数超过此值,新提交任务会被放入阻塞队列中等待调度。
3. keepAliveTime。线程池并发线程数超过核心线程数时,如果线程空闲超过时间超过此值就会被回收。
4. workQueue。阻塞队列,当然提交的任务没有足够线程调度时,任务就被加入到这个队列中。
5. threadFactory。工厂类创建线程
6. handler。线程数和队列达到限制后,就会调用handler处理,默认抛出异常。
对于不同的需求,如大量的耗时短任务或者小量的任务之间有关联等可以不同的参数。其具体工作原理如下图:
从最右边的fetch task可以看到,其核心原理就是一个简单的生产-消费者模式,即用户通过execute方法,往任务队列添加任务,然后每条线程各自从队列里获取任务执行。
经过查看源码,发现了1.7版和1.6版本的ThreadPoolExecutor,虽然接口对外完全一致,但内部的代码经过了大量的修改,总体上来说,1.7版的ThreadPoolExecutor实现比之前的JDK更加高效,代码没有1.6的实现那么清晰。具体变化如下。
一、线程池状态变量以及线程数合成一个AtomicInteger
由于线程池要实现对状态的维护以及和线程池内部运行中的线程数目,因此,1.6的实现里创建了两个volatile变量,runState、poolSize,并且每次更改这两个变量的同时,必须要加锁保护。
volatile int runState;
static final int RUNNING = 0;
static final int SHUTDOWN = 1;
static final int STOP = 2;
static final int TERMINATED = 3;
/**
* Current pool size, updated only while holding mainLock but
* volatile to allow concurrent readability even during updates.
*/
private volatile int poolSize;
对于1.7,把这两个变量合成一个AtomicInteger的原子整型变量,这样可以达到了CAS旋转锁,去除必须要加lock的做法。
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// 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; }
private static int workerCountOf(int c) { return c & CAPACITY; }
private static int ctlOf(int rs, int wc) { return rs | wc; }
/**
* Attempt to CAS-increment the workerCount field of ctl.
*/
private boolean compareAndIncrementWorkerCount(int expect) {
return ctl.compareAndSet(expect, expect + 1);
}
/**
* Attempt to CAS-decrement the workerCount field of ctl.
*/
private boolean compareAndDecrementWorkerCount(int expect) {
return ctl.compareAndSet(expect, expect - 1);
}
/**
* Decrements the workerCount field of ctl. This is called only on
* abrupt termination of a thread (see processWorkerExit). Other
* decrements are performed within getTask.
*/
private void decrementWorkerCount() {
do {} while (! compareAndDecrementWorkerCount(ctl.get()));
}
由于这个改进,在1.7实现的代码里大量的关于这两个变量的更改都变为了旋转锁,这样可以更好的提高线程池的性能。
二、Worker类的实现改进
Worker类在ThreadPoolExecutor内部是作为一个工作线程的封装类。由于每个工作线程可能在执行任务或者在等待着新的任务到来(也就是空闲线程),因此等调用setCorePoolSize等方法需要对空闲线程进行interrupt时,就必须要利用lock来对那些正在执行任务的锁进行保护。
在1.6的实现中,Worker类只是单纯实现Runnable接口,并且添加一个内部私有成员runLock的可重入锁进行保护。
private final class Worker implements Runnable {
/**
* The runLock is acquired and released surrounding each task
* execution. It mainly protects against interrupts that are
* intended to cancel the worker thread from instead
* interrupting the task being run.
*/
private final ReentrantLock runLock = new ReentrantLock();
......
}
/**
* Interrupts thread if not running a task.
*/
void interruptIfIdle() {
final ReentrantLock runLock = this.runLock;
if (runLock.tryLock()) {
try {
if (thread != Thread.currentThread())
thread.interrupt();
} finally {
runLock.unlock();
}
}
}
而在1.7的实现里,对这个进行了简单的优化,就是直接把Worker类继承自AbstractQueuedSynchronizer类,然后简单实现了一个不可重入的互斥锁,这样就不用每次都判断。
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
{
......
// Lock methods
//
// The value 0 represents the unlocked state.
// The value 1 represents the locked state.
protected boolean isHeldExclusively() {
return getState() == 1;
}
protected boolean tryAcquire(int unused) {
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
protected boolean tryRelease(int unused) {
setExclusiveOwnerThread(null);
setState(0);
return true;
}
public void lock() { acquire(1); }
public boolean tryLock() { return tryAcquire(1); }
public void unlock() { release(1); }
public boolean isLocked() { return isHeldExclusively(); }
}
对于 AbstractQueuedSynchronizer类,这个是Java里实现各种同步机制的基础类,其原理是内部实现了一个简单的线程链表,然后各种旋转CAS来更改等待状态,可重入锁ReentrantLock也是继承自这个类并实现同一线程记录上锁数的逻辑。
说回正题,这样,在interrupt空闲线程时,可以不用判断是否当前线程了。
private void interruptIdleWorkers(boolean onlyOne) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (Worker w : workers) {
Thread t = w.thread;
if (!t.isInterrupted() && w.tryLock()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
} finally {
w.unlock();
}
}
if (onlyOne)
break;
}
} finally {
mainLock.unlock();
}
}
Worker继承AbstractQueuedSynchronizer。这样也是避免当中断worker线程等待任务执行,但错误地中断了执行中的任务的中断异常。主要是在方法interruptIdleWorkers里:
private void interruptIdleWorkers(boolean onlyOne) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (Worker w : workers) {
Thread t = w.thread;
if (!t.isInterrupted() && w.tryLock()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
} finally {
w.unlock();
}
}
if (onlyOne)
break;
}
} finally {
mainLock.unlock();
}
}
该方法目的是为了中断空闲线程。首先获取mainLock,保证workers集合稳定不变,然后tryLock尝试获得线程的锁。由于worker线程在执行任务前必须获得锁,执行完成后才释放锁,这样可以确保获取锁的worker线程肯定不是在执行任务中,因此可以确保只打断空闲的线程。
其实1.6和1.7的ThreadPoolExecutor的实现还是有比较大的不同地方,尽管整个流程大体一致,但一些细微的地方1.7版还是做了优化的,目的当然是为了更好地提高性能,增加线程池的吞吐量。以上两点不同之处,仅作为本人的一点了解,旨在希望各位能够好好了解其中的优化,希望能够达到抛砖引玉的目的。