线程池的五种状态:
Running:能接受新任务,可以处理已经添加的任务。
Shutdown:不接受新任务,可以处理已经添加的任务。
Stop:不接受新任务,不处理已经添加的任务,并且中断正在处理的任务。
Tidying:所有任务已经终止,ctl记录任务数为0。(ctl负责记录线程池的运行状态与活动线程数)
Terminated:线程池彻底终止。
工作原理
ThreadPoolExecutor执行execute()方法的示意图如下:
- 如果当前运行线程小于corePoolSize,则创建新线程来执行任务(执行这一步骤需要获取全局锁)。
- 如果运行的线程等于或多于corePoolSize,则将任务加入BlockingQueue。
- 如果无法将任务加入BlockingQueue(队列已满),则创建新的线程来处理任务(执行这一步骤需要获取全局锁)。
- 如果创建新线程将使当前运行的线程超出maximumPoolSize,任务将被拒绝,并调用RejectExecutionHandler.rejectedExecution()方法。
ThreadPoolExecutor采取上述步骤,就是为了在执行execute()方法时,尽可能地避免获取全
局锁(这个是一个很严重的伸缩瓶颈)。在ThreadPoolExecutor完成预热之后(当前运行的线程数大于等于corePoolSize),几乎所有的execute()方法调用都是执行步骤2,而步骤2不需要获取全局锁。
代码片段如下:
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
//如果线程数小于corePoolSize,则创建线程并执行当前任务
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);
}
int c = ctl.get();
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;
将线程状态和活动线程数都记录在AtomicIneger里面,原子性保证并发安全。高3位存了线程状态。
线程池中的线程执行任务分两种情况:
- 在execute()方法中创建一个线程时,会让这个线程执行当前任务。
- 这个线程执行完上图1的任务后,会反复从BlockingQueue获取任务来执行。
向线程池提交任务
两种方式:execute()和submit()方法
execute()方法用于不需要返回值的任务,无法判断任务是否被线程池执行成功。
threadsPool.execute(
new Runnable(){
@Override
public void run{
//DOTO
}
}
)
execute()方法输入的是一个Runnable类的实例。
submit()方法用于提交需要返回值的任务,返回的是一个future类型的对象,通过future对象可判断任务是否执行成功,并通过future.get()方法获取返回值,get()方法会阻塞当前线程直到任务完成。
Future<Object> future = execute.submit(harReturnValuetask);
try{
Object s = future.get();
}catch(InterruptedException e){
//处理中断异常
}catch(ExecutionException e){
//处理入法执行任务异常
}finally{
//关闭线程池
execute.shutdown();
}
关闭线程池
两种方法:shutdown()获shutdownNow()
原理:遍历线程池中的工作线程,然后逐个调用线程的interrupt方法来中断线程,所以无法响应中断的任务可能永远无法终止。
区别:
- shutdownNow首先将线程池的状态设置成STOP,然后尝试所有的正在执行或暂停任务的线程,并返回等待执行任务的列表。
- shutdown只是将线程池的状态设置成STOPDOWN状态,然后中断所有没有正在执行任务的线程。
参考 --《Java并发编程的艺术》方腾飞