对于线程池来说,其提供了execute与submit两种方式来向线程池提交任务
总体来说,submit方法是可以取代execute方法的,因为它既可以接收Callable任务,也可以接收Runnable任务。
关于线程池的总体执行策略:
//这个方法在ThreadPoolExecutor的父类AbstractExecutorService中的方法
public <T> Future<T> submit(Callable<T> task) {
//空指针判断
if (task == null) throw new NullPointerException();
//完成了成员变量,和相关属性的设定,并构造了FutureTask对象
RunnableFuture<T> ftask = newTaskFor(task);
//使用execute执行任务。
execute(ftask);
return ftask;
}
//RunnableFuture接口,将任务的运行Runnable和任务执行结果Future组装在一起。
//当执行run方法后执行的结果会被封装在Future中
public interface RunnableFuture<V> extends Runnable, Future<V> {
/**
* Sets this Future to the result of its computation
* unless it has been cancelled.
*/
void run();
}
//这个方法只为了构造出来一个FutureTask
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
return new FutureTask<T>(callable);
}
//该方法就只是把callable对象赋值,且把线程状态设置为NEW = 0.
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
//是一个可取消的异步的计算,提供了对Future基本的实现。
public class FutureTask<V> implements RunnableFuture<V>
可以看到,尽管submit方法与execute方法的接收任务的类型不同,但是submit方法最终还是调用了execute方法去执行任务。不过submit对任务进行了封装使得execute在执行完任务后可用获取执行任务的return的结果。
这两个方法传入的任务类型相同故,放在一起讲。
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture<Void> ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}
public <T> Future<T> submit(Runnable task, T result) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task, result);
execute(ftask);
return ftask;
}
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
return new FutureTask<T>(runnable, value);
}
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}
//将Runnable对象转换为一个callable对象,经历这一系列转换后,这个方法和
//submit(Callable task)方法的执行就相同了。
public static <T> Callable<T> callable(Runnable task, T result) {
if (task == null)
throw new NullPointerException();
return new RunnableAdapter<T>(task, result);
}
/**
* A callable that runs given task and returns given result
*/
//这个类真正意义上实现了Runnable对象转换为一个callable对象
static final class RunnableAdapter<T> implements Callable<T> {
final Runnable task;
final T result;
RunnableAdapter(Runnable task, T result) {
this.task = task;
this.result = result;
}
//调用call方法时内部直接调用了run方法
//这里的result就是直接调用submit(Runnable task, T result)
//方法时传入的result,不过submit(Runnable task)将result置为
//null,所以之后submit(Runnable task)执行后收不到结果
//不过在整个过程中并没有对result的值进行任何的改变,这个值
//在一开始就是固定的
public T call() {
task.run();
return result;
}
}
可以看到两个方法最终都调用了newTaskFor构造了一个FutureTask,不过
通过上述分析我们可知submit(Runnable task) 只是 submit(Runnable task, T result) 方法的一个特例。而这两个方法传入的Runnable对象最终会换为一个callable对象,转换为与submit(Callable task) 方法相同的形式。而这三个方法最后都是通过execute
对于线程池来说,存在两个状态需要维护:
线程池一共存在5种状态:
线程池对任务的执行最终是由这个方法实现的,下面就对这个方法进行具体的分析。
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.
*
//线程数少于corePoolSize会尝试开启一个新的线程来执行任务addWorker方法
//会做一些同步处理
* 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();
//workerCountOf获取已经在线程池中存在的线程数量
//如果判断为true会按照第一种流程执行任务
if (workerCountOf(c) < corePoolSize) {
//尝试创建一个新的线程并且把传入的任务当成该线程的第一个
//任务去执行
if (addWorker(command, true))
return;
//创建新的线程去执行任务失败,再次获取ctl(这个值可能被其他线程修改)
c = ctl.get();
}
//此时说明存在的线程数大于或等于corePoolSize,尝试入队操作
//或虽然线程数少于corePoolSize但是提交任务失败
if (isRunning(c) && workQueue.offer(command)) {
//此时线程池正在运行且任务插入队列成功
//再次获取ctl的值
int recheck = ctl.get();
//再次判断,如果线程池不再运行,就将刚才存入阻塞队列的任务移除出阻塞队列
//并且调用reject拒绝策略处理任务
if (! isRunning(recheck) && remove(command))
reject(command);
//此时线程池正在运行,判断线程池中线程数量是否等于0
//如果等于0,调用addWorker创建新的线程执行任务
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
//执行到这说明线程池被关闭,创建新的线程执行任务,如果失败则
//执行拒绝策略
else if (!addWorker(command, false))
reject(command);
}
这个方法会在某一个时间执行任务,可能使用新建的线程,也可能使用线程池中已经存在的线程。如果任务不能被提交执行或线程池已经被关闭,或者阻塞队列已满不能存放新的任务,任务会被拒绝执行处理器接管。
//firstTask:传入的任务有两种情况:
//1.如果线程数小于corePoolSize就会创建新的线程执行任务
//2.如果阻塞队列已满也会创建新的线程执行任务
//core:true使用corePoolSize作为线程创建的边界
//false:使用maximumPoolSize作为线程数的边界
private boolean addWorker(Runnable firstTask, boolean core) {
//标签,可通过break等用法直接从后面的代码跳到此处开始执行
retry:
for (;;) {
int c = ctl.get();
//runStateOf获取线程池的状态
int rs = runStateOf(c);
// Check if queue empty only if necessary.
//线程池的状态为shutdown或跟高的状态,且队列为空,
//说明不能创建线程了
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
//到这说明线程池正在运行,可以创建线程
for (;;) {
//workerCountOf获取已有线程的数量
int wc = workerCountOf(c);
//如果线程数大于CAPACITY(最大可以创建线程数量)
//或者到了线程创建的数量边界,就才能在创建线程,
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
//尝试对线程数加1,成功在跳到retry
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 {
//把任务放入worker并且创建新的线程
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();
//线程池正常运行且worker创建的没有运行
//把线程放入workers,这个workers是一个HashSet
//用来维护所以线程池中的线程
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
//置为true表示线程已经被成功放入workers集合
workerAdded = true;
}
} finally {
mainLock.unlock();
}
//线程添加成功可以开始运行线
if (workerAdded) {
t.start();
//线程成功启动
workerStarted = true;
}
}
} finally {
//线程启动失败,把所有参数回滚到addWorker方法执行前的状态。
if (! workerStarted)
addWorkerFailed(w);
}
//workerStarted = true;表示线程添加到workers集合且
//成功执行,否则为false表示所有参数没有变化
return workerStarted;
}
/**
* Creates with given first task and thread from ThreadFactory.
* @param firstTask the first task (null if none)
*/
//创建新的线程执行任务
Worker(Runnable firstTask) {
//Worker是一个AQS子类,state置为-1是保证在创建过程中线程不会的打断
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
//在创建线程时会将Worker对象自生传入因为worker实现了Runnable
this.thread = getThreadFactory().newThread(this);
}
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
//回滚所有属性的状态
private void addWorkerFailed(Worker w) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
if (w != null)
//把创建的worker对象从workers集合中移除
workers.remove(w);
//对线程池存在的线程数减一
decrementWorkerCount();
//尝试终止线程池
tryTerminate();
} finally {
mainLock.unlock();
}
}
检查一个新的work(对线程的包装)是否能添加进来,如果添加成功worker count(线程总数)就会进行调整,且一个新的work就会被创建且启动运行firstTask就是我们传入的任务。
如果线程池处于shutdown或stop状态吗,这个方法会返回false,或者线程工厂无法 创建一个新的线程(线程工厂返回null或创建线程出现异常,如OOM异常)。就会对任务进行回滚把任务的状态恢复到一个最初的状态。
addWorker方法在线程创建成功后会调用t.start(); worker对象中的thread对象的start() 方法,而这个thread对象传入的Runnable是Worker,故在调用t.start(); 时会调用worker的run方法。
public void run() {
runWorker(this);
}
run方法调用了**runWorker(this)**方法可知这个方法就是最终执行任务的方法。
/**
* 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();
//firstTask是创建线程时传入的线程的第一个
//Task任务对象即addWorker的Runnable firstTask参数
//当然有可能为空,因为addWorker方法调用这个方法时
//有几次都没有传入具体参数,而是传入null
Runnable task = w.firstTask;
w.firstTask = null;
//解锁因为在创建worker时将worker(worker是一个AQS)的state
//设置为-1,故想要正常执行需要把state设置为>=0,而unlock().在
//执行的过程中会把state设置为0,让线程可以正常执行
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
//如果task不为null,说明创建worker对象时给了任务Task,故
//task != null判断为true,短路,就不会执行后续的task = getTask()
//如果task为null判断task != null为false,需要获取task,故执行后续的
//task = getTask()获取任务task
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判断是判断线程池是否处于shutdown或比shutdown
//更大的状态,如果是就要打断线程的执行
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
//在真正执行任务之前执行的处理
//ThreadPoolExecutor没有对其做任何处理
// **这里使用了模板设计模式,父类设计了一个模板
//却没有给出任何具体的实现,这些实现交由具体
//工程的子类来实现**
beforeExecute(wt, task);
Throwable thrown = null;
try {
//执行任务
//这个run方法就是我们在代码中显式传入的要执行的内容
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 {
//在真正执行任务之后执行的处理
//ThreadPoolExecutor没有对其做任何处理
afterExecute(task, thrown);
}
} finally {
task = null;
//增加线程处理任务数,最后做统计用
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
//处理工作线程退出
//执行到这里正常情况说明线程池的阻塞队列已经为空,没有元素了
//所以要把工作线程停止
//如果completedAbruptly = true;说明处理task中发生异常也要对work进行处理
processWorkerExit(w, completedAbruptly);
}
}
//用来处理worker
//completedAbruptly为false说明线程池中所有任务执行完成
//true表示在处理task中发生异常
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 {
//做统计把w处理的任务数加到全部的处理任务数中
completedTaskCount += w.completedTasks;
//移除w
workers.remove(w);
} finally {
mainLock.unlock();
}
//尝试设置线程池为Terminate
tryTerminate();
int c = ctl.get();
//runStateLessThan看看线程池状态是否比stop低,低说明线程池还在正常运行
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);
}
}
从阻塞队列中获取第一个元素,如果返回null,则worker必须退出,且执行的线程数减一
/**
* Performs blocking or timed wait for a task, depending on
* current configuration settings, or returns null if this worker
* must exit because of any of:
* 1. There are more than maximumPoolSize workers (due to
* a call to setMaximumPoolSize).
* 2. The pool is stopped.
* 3. The pool is shutdown and the queue is empty.
* 4. This worker timed out waiting for a task, and timed-out
* workers are subject to termination (that is,
* {@code allowCoreThreadTimeOut || workerCount > corePoolSize})
* both before and after the timed wait, and if the queue is
* non-empty, this worker is not the last thread in the pool.
*
* @return task, or null if the worker must exit, in which case
* workerCount is decremented
*/
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) :
//task获取阻塞队列第一个元素,如果阻塞会等待,直到获取
//头元素,会队列为空获取一个null
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
shutdown()会修改线程池的状态。为shutdown使线程不再接收新的任务,而把新的任务交由拒绝策略处理。 然后关闭线程池中空闲的线程,当线程池中所有任务执行完成后(正在执行线程执行的任务和阻塞队列中的任务),才在真正的关闭线程池。
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//执行权限的检查,因为有可能在运行过程中,线程的权限被改变,导致无法
//对线程池进行操作
checkShutdownAccess();
//把线程池置为shutdown状态
advanceRunState(SHUTDOWN);
//中断线程池中空闲的线程
interruptIdleWorkers();
onShutdown(); // hook for ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
//把线程池设置为Terminate状态,到这一步线程池才真正被关闭
tryTerminate();
}
//检查线程的执行权限,根据SecurityManager
private void checkShutdownAccess() {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkPermission(shutdownPerm);
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (Worker w : workers)
security.checkAccess(w.thread);
} finally {
mainLock.unlock();
}
}
}
//更改线程池状态
private void advanceRunState(int targetState) {
for (;;) {
int c = ctl.get();
//检查当前线程状态是否小于要修改的状态,如果满足
//就使用CAS修改线程池的状态
if (runStateAtLeast(c, targetState) ||
ctl.compareAndSet(c, ctlOf(targetState, workerCountOf(c))))
break;
}
}
//中断线程池在空闲的线程
private void interruptIdleWorkers() {
interruptIdleWorkers(false);
}
//中断线程池在空闲的线程,这是真正执行中断逻辑的带码
private void interruptIdleWorkers(boolean onlyOne) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//workers创建线程池中的线程时说过,这个workers管理
//所有线程池中的线程
for (Worker w : workers) {
Thread t = w.thread;
//!t.isInterrupted()判断线程是否是被中断的,如果是就不需要再次中断
//w.tryLock():检查当前线程是否正在执行,在分析线程执行任务的代码时
//线程在执行任务时会加锁,如果tryLock成功说明线程处于空闲状态
if (!t.isInterrupted() && w.tryLock()) {
try {
//将线程池中处于空闲状态的线程打断
t.interrupt();
} catch (SecurityException ignore) {
} finally {
w.unlock();
}
}
if (onlyOne)
break;
}
} finally {
mainLock.unlock();
}
}
//最终关闭线程池的方法
final void tryTerminate() {
for (;;) {
int c = ctl.get();
if (isRunning(c) ||
runStateAtLeast(c, TIDYING) ||
(runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
return;
if (workerCountOf(c) != 0) { // Eligible to terminate
interruptIdleWorkers(ONLY_ONE);
return;
}
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
try {
terminated();
} finally {
ctl.set(ctlOf(TERMINATED, 0));
//之前的代码就是主要线程池的状态设置为Terminate
//这里不再过多的解析,
//termination.signalAll();唤醒所有阻塞在
//Terminate对象上的线程,处理其逻辑
//完成线程池的最终清理
termination.signalAll();
}
return;
}
} finally {
mainLock.unlock();
}
// else retry on failed CAS
}
}
shutdownNow()方法比shutdown()更粗暴,它虽然也是对后续的任务执行拒绝策略,但是对线程池中的线程,无论是空闲还是正在运行的都会打断。然后关闭线程池。
shutdownNow()会返回线程池的阻塞队列中所有未执行任务的集合
public List<Runnable> shutdownNow() {
List<Runnable> tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//检查执行权限
checkShutdownAccess();
//设置线程状态
advanceRunState(STOP);
//打断线程池中的线程
interruptWorkers();
tasks = drainQueue();
} finally {
mainLock.unlock();
}
//清理线程池所有的资源,然后关闭线程池
tryTerminate();
return tasks;
}
//打断已经start的线程
private void interruptWorkers() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//从worker获取所有线程包装的worker对象
//并且如果线程正在执行则把线程打断
for (Worker w : workers)
w.interruptIfStarted();
} finally {
mainLock.unlock();
}
}
//打断线程的逻辑,只要线程是start且未被打断的就打断线程的执行
void interruptIfStarted() {
Thread t;
if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
}
}
}
之前讲过使用submit()方法提交Callable类型任务的时候,会生成一个新的FutureTask
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
这个task会交于execute执行,此时ftask的状态为NEW
ftask会作为线程的执行结果返回
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask);
return ftask;
}
FutureTask中的state等级
private volatile int state;
private static final int NEW = 0;
private static final int COMPLETING = 1;
private static final int NORMAL = 2;
private static final int EXCEPTIONAL = 3;
private static final int CANCELLED = 4;
private static final int INTERRUPTING = 5;
private static final int INTERRUPTED = 6;
,而交由execute的任务,如果在线程池阻塞队列已满且线程数达到maximnuPoolSize时,将任务交由拒绝策略处理,而如果采用的拒绝策略是DiscardPolicy那么就不会对ftask做任何处理。
而在以上的情况下调用
Future submit = pool.submit(new CallTest());
System.out.println("这是执行的返回结果:" + submit.get() + " 序号为:" + integer.incrementAndGet());
而Future(返回的FutureTask对象) 的get方法处理逻辑为
//
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s);
}
可以看到如果状态小于COMPLETING,就会陷入等待状态。此时就会有一个问题上述的任务如果被线程池拒绝处理,那么其FutureTask的状态会一直保持NEW(0),那么在其返回FutureTask调用get()方法会一直阻塞导致程序出错。当然如果使用get(long timeout, TimeUnit unit)就不会发生上述问题。
题出这个问题是为了说明,在我们项目使用线程池时,应该自定义Java提供的线程池的许多类,因为Java提供的只是一个模板供我们参考,线程池是如果使用的,但是Java也预留许多接口供我们实现,如果贪图简便,直接使用Java提供的线程池,那么在具体任务是可能会发生,错误,导致程序异常执行。