线程池详解-队列、抛弃策略
详细补充:《ThreadPoolExecutor 线程池源码解析以及相关理论》
无界队列
**newFixedThreadPool和newSingleThreadExecutor在默认情况下将使用一个无界的队列(LinkedBlockingQueue),**如果所有线程都在执行任务,那么任务将在队列中等待,如果任务到达的速度大于线程执行的速度,造成的后果将是队列无限期增加。
使用无界队列时注意OOM,可能导致CPU和内存飙升服务器挂掉。
有界队列
更稳妥的管理策略是使用有界队列,如:ArrayBlockingQueue,有界的LinkedBlockingQueue,PriorityBlockingQueue. PriorityBlockingQueue中的优先级由任务的Comparator决定。
使用有界队列时队列大小需和线程池大小互相配合,线程池较小有界队列较大时可减少内存消耗,降低cpu使用率和上下文切换,但是可能会限制系统吞吐量。
有界队列避免了资源耗尽的情况,但出现一个问题,队列填满后,新的任务该怎么办?使用拒绝策略。
同步移交
如果不希望任务在队列中等待而是希望将任务直接移交给工作线程,可使用SynchronousQueue作为等待队列。SynchronousQueue不是一个真正的队列,而是一种线程之间移交的机制。要将一个元素放入SynchronousQueue中,必须有另一个线程正在等待接收这个元素。只有在使用无界线程池或者有饱和策略时才建议使用该队列。
线程池的拒绝策略
JDK提供了几种不同的RejectedExecutionHandler实现,每种都是不同的饱和策略:AbortPolicy,CallerRunsPolicy,DiscardPolicy和DiscardOldestPolicy.
四种策略都做为静态内部类在ThreadPoolExcutor中进行实现。
- AbortPolicy 终止策略。当任务添加到线程池中被拒绝时,它将抛出 RejectedExecutionException 异常(继承自RuntimeException),调用者可以捕获该异常自行处理。是默认饱和策略。
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {throw new RejectedExecutionException("Task " + r.toString() +" rejected from " +e.toString());}
DiscardOldestPolicy 抛弃策略:当任务添加到线程池中被拒绝时,线程池会放弃等待队列中最旧的未处理任务(抛弃下一个将被执行的任务),然后将被拒绝的任务添加到等待队列中,如果队列是一个优先队列,那么抛弃最旧的策略就会抛弃优先级最高的任务,因此不要将两者在一起使用。
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {if (!e.isShutdown()) {e.getQueue().poll();e.execute(r);}}
DiscardPolicy 该策略默默地丢弃无法处理的任务,不予任何处理。
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {}
- CallerRunsPolicy 该策略只要线程池未关闭,该策略直接在调用者线程(主线程)中,运行当前被丢弃的任务(白话就是不会抛弃线程,也不抛出异常,而是将任务回退到调用者,从而降低新任务的流量),这样会影响QPS(Queries per second)。
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {if (!e.isShutdown()) {r.run();}}
QPS:Queries Per Second意思是“每秒查询率”,是一台服务器每秒能够相应的查询次数,是对一个特定的查询服务器在规定时间内所处理流量多少的衡量标准。
还可以自定义饱和策略。
java提供的四种常用线程池解析
1.newCachedThreadPool
在newCachedThreadPool中如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
public static ExecutorService newCachedThreadPool() {return new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());}
2. newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在无界队列中等待。
public static ExecutorService newFixedThreadPool(int nThreads) {return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());}
3. newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {return new ScheduledThreadPoolExecutor(corePoolSize);}
构造函数:
public ScheduledThreadPoolExecutor(int corePoolSize) {
//继承自 ThreadPoolExecutorsuper(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,new DelayedWorkQueue());}
DelayedWorkQueue是一个无界队列,它能按一定的顺序对工作队列中的元素进行排列。在这里,作为静态内部类就在ScheduledThreadPoolExecutor中进行了实现。
4. newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
public static ScheduledExecutorService newSingleThreadScheduledExecutor() {return new DelegatedScheduledExecutorService(new ScheduledThreadPoolExecutor(1));}
首先new了一个线程数目为1的ScheduledThreadPoolExecutor,再把该对象传入DelegatedScheduledExecutorService中,看看DelegatedScheduledExecutorService的实现代码:
DelegatedScheduledExecutorService(ScheduledExecutorService executor) {super(executor);e = executor;}
父类:
DelegatedExecutorService(ExecutorService executor) { e = executor; }
其实就是使用装饰模式增强了ScheduledExecutorService(1)的功能,不仅确保只有一个线程顺序执行任务,也保证线程意外终止后会重新创建一个线程继续执行任务。具体实现原理会在后续博客中讲解。
5.newWorkStealingPool创建一个拥有多个任务队列(以便减少连接数)的线程池。
这是jdk1.8中新增加的一种线程池实现,先看一下它的无参实现:
public static ExecutorService newWorkStealingPool() {
return new ForkJoinPool
(Runtime.getRuntime().availableProcessors(),
ForkJoinPool.defaultForkJoinWorkerThreadFactory,
null, true);
}
返回的ForkJoinPool从jdk1.7开始引进,个人感觉类似于mapreduce的思想。
jdk-1.8-ForkJoinPool实现原理 https://www.jianshu.com/p/de025df55363
资料https://blog.csdn.net/u011479540/article/details/51867886/
线程池详解-队列、抛弃策略相关推荐
- Java 线程池详解及实例代码
转载自 Java 线程池详解及实例代码 这篇文章主要介绍了Java 线程池的相关资料,并符实例代码,帮助大家学习参考,需要的朋友可以参考下 线程池的技术背景 在面向对象编程中,创建和销毁对象是很费时 ...
- java线程池详解及五种线程池方法详解
基础知识 Executors创建线程池 Java中创建线程池很简单,只需要调用Executors中相应的便捷方法即可,比如Executors.newFixedThreadPool(int nThrea ...
- 11.定时任务定时线程池详解
3.1 新增定时任务池 11.定时任务&定时线程池详解 当我们不用任务框架时,我们想自己写一个定时任务时,我们能想起那个工具类呢?Timer ?还有吗?不知道了,下面我们要讲下Schedu ...
- Java多线程之线程池详解
Java多线程之线程池详解 目录: 线程池使用及优势 线程池3个常用方式 线程池7大参数深入介绍 线程池底层工作原理 1. 线程池使用及优势 线程池做的工作主要是控制运行的线程的数量,处理过程中将任务 ...
- 干货 | Tomcat 连接数与线程池详解
转载自 干货 | Tomcat 连接数与线程池详解 前言 在使用tomcat时,经常会遇到连接数.线程数之类的配置问题,要真正理解这些概念,必须先了解Tomcat的连接器(Connector). 在 ...
- Java中线程池详解
一.线程池简介 线程池的概念 线程池就是首先创建一些线程,它们的集合称为线程池,使用线程池可以很好的提高性能,线程池在系统启动时既创建大量空闲的线程,程序将一个任务传给线程池.线程池就会启动一条线程来 ...
- Java线程池详解学习:ThreadPoolExecutor
Java线程池详解学习:ThreadPoolExecutor Java的源码下载参考这篇文章:Java源码下载和阅读(JDK1.8) - zhangpeterx的博客 在源码的目录java/util/ ...
- Java 线程池详解学习:FixedThreadPool,CachedThreadPool,ScheduledThreadPool...
Java常用的线程池有FixedThreadPool和CachedThreadPool,我们可以通过查看他们的源码来进行学习. Java的源码下载参考这篇文章:Java源码下载和阅读(JDK1.8) ...
- Executors线程池详解(全)
开场白 构造一个线程池为什么需要几个参数?如果避免线程池出现OOM? Runnable 和 Callable 的区别是什么?本文将对这些问题一一解答,同时还将给出使用线程池的常见场景和代码片段. 基础 ...
最新文章
- 用perl获取可用的代理服务器地址
- python快速入门答案-总算懂得python脚本快速入门教程
- centos mysql密码设置密码_CentOS下设置MySQL的root密码
- wgs84坐标格式转换度分秒_一起爬山吗?寻找GIS坐标系统中“隐秘的角落”
- 前端学习(2446):总页码的处理
- python多线程爬虫数据顺序_Python爬虫必学知识点:多线程爬虫
- 以太坊源码学习(一)
- 2016年2月23日----Javascript全局变量和局部变量
- bzoj3631: [JLOI2014]松鼠的新家(LCA+差分)
- 最短路问题_Dijkstra算法
- js学习笔记(新手)
- LCM模组的简介与质量管理(连载四)
- Win10常用命令:定时关机(shutdown命令)
- Windows xp sp3 补丁下载-cuyahoga
- android中屏幕保护的实现的,Android 屏幕保护程序制做及源码
- 【STC8A8K64S4A12开发板】—小白做GPIO点灯实验
- 公众号怎么提升阅读量
- Flash制作大雪纷飞效果动画
- Vue上传多张图片到服务器,数据库存储图片路径并将图片渲染到前端
- SPI 读取不同长度 寄存器_几种常用的总线设计:UART/SPI/I2C