先来看下面这个实例:
public class SimpleTask {
private final int mIndex;
private Executor mExecutors = Executors.newSingleThreadExecutor();
public SimpleTask(int index) {
mIndex = index;
}
public void runTask(int y) {
mExecutors.execute(() -> {
System.out.println(mIndex);
//System.out.println(y);
System.out.println(Thread.currentThread().getName());
});
}
public Executor getmExecutors() {
return mExecutors;
}
}
public class ThreadPoolTest {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new SimpleTask(i).runTask(i);
}
System.out.println(Thread.currentThread().getName());
System.gc();
// try {
// TimeUnit.SECONDS.sleep(10);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
}
}
执行结果为:
结果中可以看出,main执行结束了,但是整个程序处于挂起状态。 通过jmap -histo 可以看到:
执行下面代码,将SimpleTask的runTask(int y)改成下面这样:
public void runTask(int y) {
mExecutors.execute(() -> {
//System.out.println(mIndex);
System.out.println(y);
System.out.println(Thread.currentThread().getName());
});
}
程序直接结束,结果如下:
0
2
pool-3-thread-1
3
pool-4-thread-1
1
pool-2-thread-1
5
pool-6-thread-1
main
4
pool-5-thread-1
pool-1-thread-1
9
pool-10-thread-1
7
pool-8-thread-1
8
pool-9-thread-1
6
pool-7-thread-1
在main方法中加上sleep逻辑,让主程序挂起一段时间,然后执行jmap -histo : main方法代码如下:
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new SimpleTask(i).runTask(i);
}
System.out.println(Thread.currentThread().getName());
System.gc();
try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
然后执行jmap -histo :
226: 4 64 com.rt.platform.infosys.market.SimpleTask$$Lambda$1/792791759
403: 1 16 java.util.concurrent.ThreadPoolExecutor$AbortPolicy
36: 18 6768 java.lang.Thread
84: 10 720 java.util.concurrent.ThreadPoolExecutor
98: 10 480 java.util.concurrent.LinkedBlockingQueue
99: 10 480 java.util.concurrent.ThreadPoolExecutor$Worker
100: 30 480 java.util.concurrent.locks.ReentrantLock
106: 1 384 com.intellij.rt.execution.application.AppMainV2$1
代码改成这样:
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
ExecutorService executorService = Executors.newSingleThreadExecutor();
int finalI = i;
executorService.execute(() -> {
SimpleTask simpleTask = new SimpleTask(finalI);
System.out.println(simpleTask.getmIndex());
});
}
try {
//给System.gc()提供一定的时间
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
System.gc();
try {
TimeUnit.SECONDS.sleep(30);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
结果为:
9
1
5
2
3
8
0
4
7
6
main
执行jmap -histo 结果为:
84: 10 720 java.util.concurrent.ThreadPoolExecutor
98: 10 480 java.util.concurrent.LinkedBlockingQueue
99: 10 480 java.util.concurrent.ThreadPoolExecutor$Worker
100: 30 480 java.util.concurrent.locks.ReentrantLock
线程池无法被回收,是因为线程池的引用被它的内部类 Worker 持有了。而 Worker 和线程一一对应,是对 Thread 的增强,所以本质上就是因为线程没有被释放。 看java.util.concurrent.ThreadPoolExecutor.runWorker代码:
/**
* Main worker run loop. Repeatedly gets tasks from queue and
* executes them, while coping with a number of issues:
*
* 1. We may start out with an initial task, in which case we
* don't need to get the first one. Otherwise, as long as pool is
* running, we get tasks from getTask. If it returns null then the
* worker exits due to changed pool state or configuration
* parameters. Other exits result from exception throws in
* external code, in which case completedAbruptly holds, which
* usually leads processWorkerExit to replace this thread.
*
* 2. Before running any task, the lock is acquired to prevent
* other pool interrupts while the task is executing, and then we
* ensure that unless pool is stopping, this thread does not have
* its interrupt set.
*
* 3. Each task run is preceded by a call to beforeExecute, which
* might throw an exception, in which case we cause thread to die
* (breaking loop with completedAbruptly true) without processing
* the task.
*
* 4. Assuming beforeExecute completes normally, we run the task,
* gathering any of its thrown exceptions to send to afterExecute.
* We separately handle RuntimeException, Error (both of which the
* specs guarantee that we trap) and arbitrary Throwables.
* Because we cannot rethrow Throwables within Runnable.run, we
* wrap them within Errors on the way out (to the thread's
* UncaughtExceptionHandler). Any thrown exception also
* conservatively causes thread to die.
*
* 5. After task.run completes, we call afterExecute, which may
* also throw an exception, which will also cause thread to
* die. According to JLS Sec 14.20, this exception is the one that
* will be in effect even if task.run throws.
*
* The net effect of the exception mechanics is that afterExecute
* and the thread's UncaughtExceptionHandler have as accurate
* information as we can provide about any problems encountered by
* user code.
*
* @param w the worker
*/
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 pool is stopping, ensure thread is interrupted;
// if not, ensure thread is not interrupted. This
// requires a recheck in second case to deal with
// shutdownNow race while clearing interrupt
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);
}
}
如果想要执行processWorkerExit方法需要满足两个条件:
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;
}
}
}
有两种情况getTask会为null: