JAVA线程池
- 为什么会有线程池
- JAVA线程与系统线程的映射方式
- 多对一映射,多个线程被认为一个系统核心线程,由系统调度,这多个线程在jvm内部进行调度,避免使用系统资源进行调度,快速而又安全。
-
- 一对一映射,在cpu多核时代,上面的模式显然只能同时使用一个cpu核心,于是出现了一个java线程对应一个系统核心线程的方案。这样可以发挥多核的作用,不同线程运行在不同的核心上,但缺点是由系统管理线程,开销太大。
- 线程的代价
- 核心线程的消耗过大。默认一个线程栈1M内存,1024个就是1G了。
- 创建一个核心线程消耗很大,需要分配内存,列入调度等。
- 核心线程的数量越多,操作系统将会花费更多时间去理清线程之间的关系。
- java线程池的诞生
- 为了缓解不断重复的创建和销毁线程,以及可能出现的数量巨大的线程,java创造了线程池的解决方案。
- JAVA线程池
- 核心线程数:java线程池保有的最小线程数量。
- 任务队列:排队等待解决的任务。
- 最大线程数量:当队伍排满后,允许创建临时的线程来解决任务。
- 临时线程过期时间:临时线程闲置一定时间后,将被销毁。
- 拒绝策略:当队列已满,线程池已满时,采用什么样的策略。
- 线程池的工作流程
- 如果线程池中的线程数量小于核心线程数,会直接创建新的线程。
- 如果线程池中的线程数量大于核心线程数,会将线程中的任务存储到任务队列中去。
- 当队列排满后,且线程数小于最大线程数量,就会创建新的线程;如果线程数大于等于最大线程数量,就会采用拒绝策略。
- 拒绝策略
- AbortPolicy
简单粗暴,直接抛出拒绝异常,这也是默认的拒绝策略。 - CallerRunsPolicy
如果线程池未关闭,则会在调用者线程中直接执行新任务,这会导致主线程提交线程性能变慢。 - DiscardPolicy
从方法看没做任务操作,即表示不处理新任务,即丢弃。 - DiscardOldestPolicy
抛弃最老的任务,就是从队列取出最老的任务然后放入新的任务进行执行。
- 创建线程池
- 线程池分类,类Executors,是java创建线程池的工厂类,提供了4种线程池。
- newFixedThreadPool 固定线程池,核心线程数和最大线程数相等。
- newCachedThreadPool 缓冲线程池,核心线程数为0,所有线程都是临时线程,超过空闲时间会销毁。
- newSingleThreadExecutor 单线程线程池,核心线程数和最大线程数都为1。
- newScheduledThreadPool 调度线程池,即按一定的周期执行任务,即定时任务。
- 线程池分类,类Executors,是java创建线程池的工厂类,提供了4种线程池。
但是使用Executors提供的方法创建的线程池,使用的默认任务队列是无界的,这样有可能因为队列太长而导致内存溢出。
因此最好的的创建线程池的方法是直接使用线程池的构造函数来实例化线程池,创建的过程中传入一个有界的队列,就可以有效避免以上问题了。
- 线程池任务提交
- Submit 返回一个Future对象。
- Execute 无返回值。
- 关闭线程池
- Shutdown:不再接受新任务,任务全部完成结束
- shutdownNow:不再接受新的任务,试图停止池中的任务再关闭线程池,返回所有未处理的线程list列表