1.线程池机制
当我们的线程数量过大时,频繁的线程开启和关闭会消耗额外的资源。通过线程池,可以不用去频繁的开启和关闭线程。当有一个线程要开启时,通过线程池中的线程开启。这个线程运行完毕时,将线程归还给线程池,用于后来的线程使用。
通过execute() \ submit() 提交给线程池
2.创建线程池
2.1ThreadPoolExecutor线程池参数
我们通过 ThreadPoolExecutor 去创建一个线程池
先来看看 ThreadPoolExecutot 对象的参数
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler);
-
corePoolSize
核心线程数,这些线程是不会被回收的,一直存在于线程池中 -
maximumPoolSize
最大线程数,在超出和弦线程池时,在工作队列已满的情况下,会创建额外的线程,这些线程的数量由求最大线程数确定 -
keepAliveTime
额外线程存留时间,当创建了超出核心线程数的线程,当这些线程执行完任务后,超出了这个时间,将会被回收 -
unit
时间单位,线程池存留时间的时间单位 -
workQueue
任务队列,超出核心线程数的线程,会被分配到任务队列中等待,直到有现成执行完,该线程获取到线程时出队。这些任务队列都是BlockingQueue 的实现类,BlockingQueue 是一个阻塞队列,当有一个线程正在入队或出队时,其他线程不想允许操作。确保了线程安全。
线程池工厂,设线程池组,与线程池内的线程名前缀
-
handler
拒绝策略方法,线程数量超出最大线程数时执行拒绝策略。有以下四种拒绝策略:-
AbortPolicy
丢弃当前线程,并抛出异常
-
DiscardPolicy
丢弃当前线程,不抛出异常
-
DiscardOldestPolicy
拿到一个最“老"的线程,用来执行当前县城任务。先检测线程池是否关闭,然后再获取线程,执行任务
-
2.2ThreadPoolExecutor线程池创建
还可以通过 Executors 执行器自动的去创建线程池。在 Executors 类中定义了大量的创建线程池的模板方法。在以下举几个例子说明:
-
Executors.newFixedThreadPool()
创建固定核心线程数的线程池public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
我们可以看到,通过一个int类型的参数,设置了核心线程数和最大线程数。
-
Executors.newSingleThreadExecutor()
创建只有一个线程数的线程池
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }
这个方法没有参数,在方法的内部固定了核心线程数和最大线程数为1
-
Executors.newCachedThreadPool()
动态创建线程的线程池public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
相对前几个来说,该方法创建的线程池是较为特殊的一个线程池,该线程池的核心线程数为0,最大线程数为 Integer.MAX_VALUE=2147483647。这个线程池没有 核心线程数,并且该任务队列也不会存储任务队列,会直接判断线程数是否超过最大线程数,去创建新的该线程。
- Executors.newScheduledThreadPool()
用于需要定时任务调度和周期性任务执行的场景
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
这个方法创建的是 ScheduledThreadPoolExecutor,为ThreadPoolExecutor的子类
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
3.线程池执行流程
线程池在工作时会根据设置的参数去做线程调度,共有以下步骤:
- 在向线程池提交任务时,判断是否存在空闲线程
- 空闲线程存在,分配空闲线程,执行该线程
- 空闲线程不存在,判断已有线程是否超出设置的核心线程数
- 核心线程数未超出,创建新的核心线程去执行该线程任务
- 已超出,判断任务队列是否已满
- 任务队列未满,将该线程任务放入工作队列中等待,等有其他线程执行任务后,归还线程,工作队列中的任务依次出队,获取空闲线程,执行线程任务
- 任务队列满,判断是否超出最大线程数
- 最大线程数未超出,创建非核心线程,执行线程任务,该线程执行完任务,空闲的时间超出指定时间后将被回收
- 已超出,执行拒绝策略
4.线程池状态
在源码描述中,线程池有五种状态,分别是RUNNING,SHUTDOWN,STOP,TIDYING,TERMINATED
- RUNNING
- 运行状态,允许新任务,并处理工作队列中的任务
- 可调用 shutdown()方法,切换到关闭状态
- 调用 shutdownNow()方法,切换到停止状态
- SHUTDOWN
- 关闭状态,不接受新任务,会继续处理工作队列红的任务
- STOP
- 停止状态,不接受新任务,也不处理工作队列中的任务,同时终端正在执行的任务
- TIDYING
- 整理状态,所有任务已经结束,工作队列中的任务数量为0
- TERMINARTED
- 终止状态,线程池彻底关闭