线程池学习总结
参考文章:
讲解线程池的思路;
一、线程池的好处(为什么要使用线程池)
这里借用《Java 并发编程的艺术》提到的来说一下使用线程池的好处:
- 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
- 提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即执行。
- 提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
二、线程池的类和接口的介绍
Executor: 所有线程池的接口,只有一个方法。
ExecutorService: 增加Executor的行为,是Executor实现类的最直接接口。
Executors: 创建线程池的静态工厂类,提供了一系列工厂方法用于创先线程池,返回的线程池都实现了ExecutorService 接口。但是一般不推荐使用这个工厂类,因为Executors 提供的很多方法默认使用的都是无界的 LinkedBlockingQueue,高负载情境下,无界队列很容易导致 OOM,而 OOM 会导致所有请求都无法处理,这非常致命。所以最好使用有界队列。
ThreadPoolExecutor:线程池的具体实现类,一般用的各种线程池都是基于这个类实现的。
2.1 ThreadPoolExecutor 构造方法参数
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
2.1 拒绝策略
三、Java 线程池工作过程
线程池的宏观图
四、JAVA 阻塞队列原理
4.1 主要方法
4.2 插入操作:
4.3 获取数据操作 :
4.4 Java 中的阻塞队列
1. ArrayBlockingQueue(公平、非公平)
ArrayBlockingQueue fairQueue = new ArrayBlockingQueue(1000,true);
2. LinkedBlockingQueue(两个独立锁提高并发)
3. PriorityBlockingQueue(compareTo 排序实现优先)
4. SynchronousQueue(不存储数据、可用于传递数据)
5. DelayQueue(缓存失效、定时任务 )
五、四种常见的线程池:
CachedThreadPool 和 SecudleThreadPool 的缺点是
允许创建的线程数量为 Integer.MAX_VALUE ,可能会创建大量线程,从而导致 OOM。CachedThreadPool 的参数情况
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
FixedThreadPool 的 参数情况
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
SingleThreadPool 的参数情况
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }
ScheduledThreadPool
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) { return new ScheduledThreadPoolExecutor(corePoolSize); } public ScheduledThreadPoolExecutor(int corePoolSize) { super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, // super 就是 ThreadPoolExecutor new DelayedWorkQueue()); }
Executors创建线程池的【弊端】
FixedThreadPool 、SingleThreadExecutor 中等待队列长度设置为Integer.MAX_VALUE
CachedThreadPool 和 ScheduledThreadPool 允许最大线程数设置为 Integer.MAX_VALUE
这两者都可能造成 OOM的问题
六、常用方法比较
6.1、shutdown()
VSshutdownNow()
shutdown()
:关闭线程池,线程池的状态变为SHUTDOWN
。线程池不再接受新任务了,但是队列里的任务得执行完毕。shutdownNow()
:关闭线程池,线程的状态变为STOP
。线程池会终止当前正在运行的任务,并停止处理排队的任务并返回正在等待执行的 List。
6.2、isTerminated()
VS isShutdown()
isShutDown
当调用shutdown()
方法后返回为 true。isTerminated
当调用shutdown()
方法后,并且所有提交的任务完成后返回为 true
6.1 callable 和 runnable 的区别
callable 可返回值||抛出异常,runnable不行,callable常用于异步计算
6.2 submit 和 execute 区别
execute提交【无返回值】的任务,无法根据返回值判断任务是否成功执行
submit提交【有返回值】的任务,根据返回值判断任务是否成功执行
七、线程池的状态
7.1,5种状态
线程池状态定义代码如下:
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
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;
private static int ctlOf(int rs, int wc) { return rs | wc; }
说明:
ctl是一个AtomicInteger类型的原子对象。ctl记录了"线程池中的任务数量"和"线程池状态"2个信息。
ctl共包括32位。其中,高3位表示"线程池状态",低29位表示"线程池中的任务数量"。
RUNNING -- 对应的高3位值是111。
SHUTDOWN -- 对应的高3位值是000。
STOP -- 对应的高3位值是001。
TIDYING -- 对应的高3位值是010。
TERMINATED -- 对应的高3位值是011。
线程池各个状态之间的切换如下图所示:
1. RUNNING
(01) 状态说明:线程池处在RUNNING状态时,能够接收新任务,以及对已添加的任务进行处理。
(02)
状态切换:线程池的初始化状态是RUNNING。换句话说,线程池被一旦被创建,就处于RUNNING状态!
道理很简单,在ctl的初始化代码中(如下),就将它初始化为RUNNING状态,并且"任务数量"初始化为0。
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
2. SHUTDOWN
(01) 状态说明:线程池处在SHUTDOWN状态时,不接收新任务,但能处理已添加的任务。
(02)
状态切换:调用线程池的shutdown()接口时,线程池由RUNNING -> SHUTDOWN。
3. STOP
(01)
状态说明:线程池处在STOP状态时,不接收新任务,不处理已添加的任务,并且会中断正在处理的任务。
(02)
状态切换:调用线程池的shutdownNow()接口时,线程池由(RUNNING or SHUTDOWN ) ->
STOP。
4.
TIDYING
(01)
状态说明:当所有的任务已终止,ctl记录的"任务数量"为0,线程池会变为TIDYING状态。当线程池变为TIDYING状态时,会执行钩子函数terminated()。terminated()在ThreadPoolExecutor类中是空的,若用户想在线程池变为TIDYING时,进行相应的处理;可以通过重载terminated()函数来实现。
(02)
状态切换:当线程池在SHUTDOWN状态下,阻塞队列为空并且线程池中执行的任务也为空时,就会由 SHUTDOWN ->
TIDYING。
当线程池在STOP状态下,线程池中执行的任务为空时,就会由STOP
-> TIDYING。
5.
TERMINATED
(01)
状态说明:线程池彻底终止,就变成TERMINATED状态。
(02)
状态切换:线程池处在TIDYING状态时,执行完terminated()之后,就会由 TIDYING ->
TERMINATED。