每日一个小道理:多读书多总结,好记性真的不如烂笔头。
好了,下面进入正题。
1. 线程池简介
线程池:
“线程池”,顾名思义就是一个线程缓存,线程是稀缺资源,如果被无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,因此Java中提供线程池对线程进行统一分配、调优和监控;
1.2 线程池介绍
在web开发中,服务器需要接受并处理请求,所以会为一个请求来分配一个线程来进行处理。如果每次请求都新创建一个线程的话实现起来非常简便,但是存在一个问题:如果并发的请求数量非常多,但每个线程执行的时间很短,这样就会频繁的创建和销毁线程,如此一来会大大降低系统的效率。可能出现服务器在为每个请求创建新线程和销毁线程上花费的时间和消耗的系统资源要比处理实际的用户请求的时间和资源更多。那么有没有一种办法使执行完一个任务,并不被销毁,而是可以继续执行其他的任务呢?这就是线程池的目的了。线程池为线程生命周期的开销和资源不足问题提供了解决方案。通过对多个任务重用线程,线程创建的开销被分摊到了多个任务上。
1.3 什么时候使用线程池?
单个任务处理时间比较短
需要处理的任务数量很大
1.4 线程池优势
重用存在的线程,减少线程创建,消亡的开销,提高性能
提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即执行。
提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
2.Executor框架
Executor接口是线程池框架中最基础的部分,定义了一个用于执行Runnable的execute方法
其中ExecutorService是一个总的子接口;其中定义了重要的方法:
public interface ExecutorService extends Executor {
//在完成已提交的任务后封闭办事,不再接管新任务
void shutdown();
//停止所有正在履行的任务并封闭办事
List<Runnable> shutdownNow();
//测试是否该ExecutorService已被关闭
boolean isShutdown();
//测试是否所有任务都履行完毕了
boolean isTerminated();
boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException;
//可用来提交Callable或Runnable任务,并返回代表此任务的Future对象
<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
throws InterruptedException;
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException;
<T> T invokeAny(Collection<? extends Callable<T>> tasks)
throws InterruptedException, ExecutionException;
<T> T invokeAny(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
2.1 线程池存在5种状态
RUNNING
SHUTDOWN
STOP
TIDYING
TERMINATED
1、RUNNING
状态说明:线程池处在RUNNING状态时,能够接收新任务,以及对已添加的任务进行处理。
状态切换:线程池的初始化状态是RUNNING。换句话说,线程池被一旦被创建,就处于RUNNING状态,并且线程池中的任务数为0!
2、 SHUTDOWN
状态说明:线程池处在SHUTDOWN状态时,不接收新任务,但能处理已添加的任务。
状态切换:调用线程池的shutdown()接口时,线程池由RUNNING -> SHUTDOWN。
3、STOP
状态说明:线程池处在STOP状态时,不接收新任务,不处理已添加的任务,并且会中断正在处理的任务。
状态切换:调用线程池的shutdownNow()接口时,线程池由(RUNNING orSHUTDOWN ) -> STOP。
4、TIDYING
状态说明:当所有的任务已终止,ctl记录的”任务数量”为0,线程池会变为TIDYING状态。当线程池变为TIDYING状态时,会执行钩子函数terminated()。terminated()在ThreadPoolExecutor类中是空的,若用户想在线程池变为TIDYING时,进行相应的处理;可以通过重载terminated()函数来实现。
状态切换:当线程池在SHUTDOWN状态下,阻塞队列为空并且线程池中执行的任务也为空时,就会由 SHUTDOWN -> TIDYING。 当线程池在STOP状态下,线程池中执行的任务为空时,就会由STOP -> TIDYING。
5、 TERMINATED
状态说明:线程池彻底终止,就变成TERMINATED状态。
状态切换:线程池处在TIDYING状态时,执行完terminated()之后,就会由 TIDYING -> TERMINATED。
进入TERMINATED的条件如下:
线程池不是RUNNING状态;
线程池状态不是TIDYING状态或TERMINATED状态;
如果线程池状态是SHUTDOWN并且workerQueue为空;
workerCount为0;
设置TIDYING状态成功。
3. 线程池创建:
3.1 固定数量的线程池
/**
*创建重复使用固定数量线程的线程池
*使用提供的
*在需要时创建新线程的ThreadFactory。在任何时候,
*最多{@code nThreads}线程将处于活动处理状态
*任务。如果在所有线程
*活动,它们将在队列中等待线程
*可用。如果任何线程在
*在关闭之前执行,如果
*需要执行后续任务。池中的线程将
*存在,直到显式{@ Link ExcutoService }shutdown
* shutdown}.
*@param nThreads 线程池中的线程数
*@param threadFactory创建新线程时要使用的工厂
*@return 返回新创建的线程池
*@threadFactory为空时抛出NullPointerException
*如果{@code nThreads<=0},则@throws IllegalArgumentException
*/
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory);
}
3.2.并行线程池
这个是1.8中新加入的线程池,可以看到主要使用了ForkJoin相关的操作
/**
* 并行线程池
* 作为其目标并行级别。
*@返回新创建的线程池
* @see #newWorkStealingPool(int)
* @since 1.8
*/
public static ExecutorService newWorkStealingPool() {
return new ForkJoinPool
(Runtime.getRuntime().availableProcessors(),
ForkJoinPool.defaultForkJoinWorkerThreadFactory,
null, true);
}
3.只有一个线程的线程池
其实这个可以不称为线程池,因为只有一个线程。核心线程=最大线程=1,这个比较适合需要保证队列中任务顺序执行的场景。
/**
*创建使用单个工作线程操作的执行器
*从无限的队列中。(但请注意,如果
*线程在执行之前由于失败而终止
*关闭,如果需要执行新的
*后续任务。)保证执行任务
*按顺序,任何时候都不会有多个任务处于活动状态
*给定的时间。与其他等价物不同
*{@code newFixedThreadPool(1)}返回的执行器是
*保证不可重新配置以使用其他线程。
*
* @return the newly created single-threaded Executor
*/
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
4.缓存线程池
他根据需要创建线程,没有核心线程,当60s内没有任务时,将会回收存活的线程,60s内有任务时,他可以重用已有的线程。注意他的工作队列是SynchronousQueue,这里简单的说一下),他的每一个put操作必须等待take操作,这意味着如果任务生产速度大于消费速度,那么他将不会创建新线程。该线程池适合执行大量小任务的场景。
/**
*创建根据需要创建新线程的线程池,但是
*将重用以前构造的线程
*可用。这些池通常会提高性能
*执行许多短期异步任务的程序。
*对{@code execute}的调用将重用先前构造的
*线程(如果可用)。如果没有现有线程可用,则新建
*将创建线程并将其添加到池中。具有
*60秒未使用被终止并从
*缓存。因此,一个空闲时间足够长的池将
*不消耗任何资源。请注意,与
*属性,但有不同的详细信息(例如,超时参数)
*可以使用{@link ThreadPoolExecutor}构造函数创建。
* @return the newly created thread pool
*/
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
5.延时线程池
ScheduledThreadPoolExecutor继承了ThreadPoolExecutor,所以super最终会调到ThreadPoolExecutor的构造函数,可以看到,最大线程数为int最大值,工作队列为延时队列DelayedWorkQueue,该线程池适合执行延时任务
/**
*创建可以调度命令的单线程执行器
*在给定的延迟后运行,或周期性地执行。
*(但请注意,如果
*线程在执行之前由于失败而终止
*关闭,如果需要执行新的
*后续任务。)保证执行任务
*按顺序,任何时候都不会有多个任务处于活动状态
*给定的时间。与其他等价物不同
*{@code newScheduledThreadPool(1)}返回的执行器是
*保证不可重新配置以使用其他线程。
*@返回新创建的计划执行器
*/
public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
return new DelegatedScheduledExecutorService
(new ScheduledThreadPoolExecutor(1));
}
public static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory) {
return new DelegatedScheduledExecutorService
(new ScheduledThreadPoolExecutor(1, threadFactory));
}