什么是线程池
线程池:提供了一个线程队列。队列中保存了所有等待状态的线程,避免了创建与销毁的额外开销,提高了响应速度
线程池的体系结构
线程池的优势
线程的复用、控制线程的并发数、管理线程
线程池工具类 Executors(三大方法)
- newFixedThreadPool:创建一个固定线程的线程池
- newSingleThreadExecutor:创建单个线程池,线程池中只有一个线程
- newCachedThreadPool:缓存线程池,线程数量不固定,可以根据需求自动更改数量
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory));
}
七大参数
public ThreadPoolExecutor(int corePoolSize,//核心线程池大小
int maximumPoolSize,//最大核心线程池大小
long keepAliveTime,//空闲线程的保持活跃时间,超时就会释放该线程
TimeUnit unit,//时间单位
BlockingQueue<Runnable> workQueue,//阻塞队列
ThreadFactory threadFactory,//创建线程的工厂,一般不用动
RejectedExecutionHandler handler) {//拒绝策略
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
- newScheduledThreadPool:创建一个固定大小的线程池,可以延迟或定期执行的执行任务,类似Timer。
四种拒绝策略
什么时候触发拒绝策略呢?
当线程池的使用量已经达到了整个池子最大线程数(最大核心线程数 + 阻塞队列),此时还有线程进来,就会触发拒绝策略
- AbortPolicy:不处理,直接抛异常
- DiscardOldestPolicy:尝试去和最早的竞争,不会抛出异常
- CallerRunsPolicy:从哪个线程来的就回哪个线程去,让原来的线程处理
- DiscardPolicy:直接丢掉任务,不会抛出异常
池的最大大小该怎么设置?
CPU密集型:定义为电脑的处理器个数,可以保持CPU的效率最高
IO密集型:判断特别消耗io的线程的个数,定位为它的两倍
线程池的状态
在类ThreadPoolExecutor中可以看到线程池的状态
线程池的5种状态:RUNNING、SHUTDOWN、STOP、TIDYING、TERMINATED。
状态解释
/* RUNNING: Accept new tasks and process queued tasks
* SHUTDOWN: Don't accept new tasks, but process queued tasks
* STOP: Don't accept new tasks, don't process queued tasks,
* and interrupt in-progress tasks
* TIDYING: All tasks have terminated, workerCount is zero,
* the thread transitioning to state TIDYING
* will run the terminated() hook method
* TERMINATED: terminated() has completed
// 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;
状态间的变化
* RUNNING -> SHUTDOWN
* On invocation of shutdown(), perhaps implicitly in finalize()
* (RUNNING or SHUTDOWN) -> STOP
* On invocation of shutdownNow()
* SHUTDOWN -> TIDYING
* When both queue and pool are empty
* STOP -> TIDYING
* When pool is empty
* TIDYING -> TERMINATED
* When the terminated() hook method has completed
Threads waiting in awaitTermination() will return when the state reaches TERMINATED.
状态转换图
RUNNING:
线程池处在RUNNING状态时,能够接收新任务,以及对已添加的任务进行处理。
线程池被一旦被创建,就处于RUNNING状态,并且线程池中的任务数为0!
SHUTDOWN
线程池处在SHUTDOWN状态时,不接收新任务,但能处理已添加的任务
调用线程池的shutdown()接口时,线程池由RUNNING -> SHUTDOWN
STOP
线程池处在STOP状态时,不接收新任务,不处理已添加的任务,并且会中断正在处理的任务。
调用线程池的shutdownNow()接口时,线程池由(RUNNING or SHUTDOWN ) -> STOP。
TIDYING
当所有的任务已终止,ctl记录的“任务数量”为0,线程池会变为TIDYING状态。当线程池变为TIDYING状态时,会执行钩子函数terminated()。terminated()在ThreadPoolExecutor类中是空的,若用户想在线程池变为TIDYING时,进行相应的处理;可以通过重载terminated()函数来实现。
当线程池在SHUTDOWN状态下,阻塞队列为空并且线程池中执行的任务也为空时,就会由 SHUTDOWN -> TIDYING。
当线程池在STOP状态下,线程池中执行的任务为空时,就会由STOP -> TIDYING。
TERMINATED
线程池彻底终止,就变成TERMINATED状态
线程池处在TIDYING状态时,执行完terminated()之后,就会由 TIDYING -> TERMINATED。