转载请标明出处:
https://blog.csdn.net/bingospunky/article/details/80234457
本文出自马彬彬的博客
创建ThreadPoolExecutor
ExecutorService fixedThreadPool = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(1));
我们使用Executors.newXXX方法创建线程池也是通过上面的new方法进行创建的,只是传递的初始化参数不同罢了。
第1、2个参数是线程池线程的数量,第3、4个参数是当线程没有runnable执行时,多长时间结束,第5个参数是当runnable多于线程数时,把runnable存放的队列。
原理
ThreadPoolExecutor有一个private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));参数表示当前执行的线程,ctl是一个32位的整数,AtomicInteger是为了线程安全。这个ctl前3位相当于占位功能,后29位表示当前启动的线程数量,比如11100000000000000000000000000000表示没有执行的线程,11100000000000000000000000000001表示当前有一个线程在执行。当添加线程或者线程结束时,会响应的改变ctl的值。
有一个Worker的类,这个Worker继承AbstractQueuedSynchronizer,实现锁的功能,在执行runnable时互斥。这个Worker包含一个Thread,Thread的run方法是while循环,不断的从queue里获取runnable执行。
当我们执行的runnable数量 超过线程数+queue.size 时如何
当我们使用Executors.newXXX创建线程池时一般不会遇到这个问题,因为这样创建的队列容量是Integer.MAX,当我们想控制线程池的queue容量时,就有可能会遇到超过上限的这种情况。
当出现这种情况时,会抛出异常。execute方法代码如下:
Code1
ThreadPoolExecutor.ThreadPoolExecutor
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);
}
我就不自己解释了,借用一下代码里的一段注释来解释。
/*
* 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.
*/
当我们的runnable出现异常时如何处理的
从使用经验上来看,我们的runnable方法抛出了异常,后面的runnable依然可以执行。那么这是如何实现的呢。
首先来看Worker里的Thread的run方法。
Code2
ThreadPoolExecutor.runWorker
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
while (task != null || (task = getTask()) != null) {
w.lock();
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
beforeExecute(wt, task);
Throwable thrown = null;
try {
task.run();
} catch (RuntimeException x) {
thrown = x; throw x;
} catch (Error x) {
thrown = x; throw x;
} catch (Throwable x) {
thrown = x; throw new Error(x);
} finally {
afterExecute(task, thrown);
}
} finally {
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
}
Code2第19行抛出异常后,会执行第37行的processWorkerExit方法,这时completedAbruptly=true,表示这时异常结束的前面的while循环。我们再看一下processWorkerExit方法的代码。
Code3
ThreadPoolExecutor.processWorkerExit
private void processWorkerExit(Worker w, boolean completedAbruptly) {
if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
decrementWorkerCount();
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
completedTaskCount += w.completedTasks;
workers.remove(w);
} finally {
mainLock.unlock();
}
tryTerminate();
int c = ctl.get();
if (runStateLessThan(c, STOP)) {
if (!completedAbruptly) {
int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
if (min == 0 && ! workQueue.isEmpty())
min = 1;
if (workerCountOf(c) >= min)
return; // replacement not needed
}
addWorker(null, false);
}
}
由于completedAbruptly值为true,先执行第3行代码,改变前文中提到的ctl的值,让其表示的线程数减1。Code3第8行,remove掉该Worker。会执行Code3第22行代码,使用runnable=null,添加一个Worker对象,这句话的意思就是启动一个新的线程,去queue里获取runnable执行。
所以,我们自己runnable抛出了异常,是kill掉了执行它的线程,并且补充一个新的线程。
ThreadPoolExecutor构造方法里的第3、4个方法如何生效
ThreadPoolExecutor第3、4个方法表示当线程空闲多久后kill。
这个线程kill是针对多余core线程数生效的,或则我们可以设置对于所有线程生效。如果设置对于所有线程生效,我们需要执行ThreadPoolExecutor.allowCoreThreadTimeOut(true)设置。
那么这是如何生效的呢。我们还是来看Code2的代码。该线程一直在while里执行,当它跳出while时,就是线程结束的条件。跳出while的条件就是Code2第8行的getTask方法返回null。接下来看getTask方法。
Code4
ThreadPoolExecutor.getTask
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;
}
}
}
Code4第13行,当allowCoreThreadTimeOut或者当先线程数超过core线程数时,设置timed=true。Code第21行,当timed==true时,queue获取runnable时阻塞我们设置的时间。这样来实现当线程空闲某一段时间后,线程kill的。
补充
ThreadPoolExecutor记录线程数量等使用的是一个AtomicInteger类型的变量ctl,把这个变量前3位和后29位分开使用的,使用了位运算,下面的代码方面我们查看数据的变化。
System.out.println(-536870912 & 536870911);
System.out.println(-536870912 & ~536870911);
System.out.println(Integer.toBinaryString(-536870912));
System.out.println(Integer.toBinaryString(-536870911));
System.out.println(Integer.toBinaryString(536870911));
System.out.println(Integer.toBinaryString(536870912));
System.out.println(Integer.toBinaryString(-1));
System.out.println(Integer.toBinaryString(-1));
System.out.println(Integer.toBinaryString(-1 << 29));
System.out.println(Integer.toBinaryString((-1 << 29) | 0));