java线程池与五种常用线程池策略使用与解析
背景:面试中会要求对5中线程池作分析。所以要熟知线程池的运行细节,如CachedThreadPool会引发oom吗?
可选择的阻塞队列BlockingQueue详解
首先看一下新任务进入时线程池的执行策略: 
如果运行的线程少于corePoolSize,则 Executor始终首选添加新的线程,而不进行排队。(如果当前运行的线程小于corePoolSize,则任务根本不会存入queue中,而是直接运行) 
如果运行的线程大于等于 corePoolSize,则 Executor始终首选将请求加入队列,而不添加新的线程。 
如果无法将请求加入队列,则创建新的线程,除非创建此线程超出 maximumPoolSize,在这种情况下,任务将被拒绝。 
主要有3种类型的BlockingQueue:
无界队列
队列大小无限制,常用的为无界的LinkedBlockingQueue,使用该队列做为阻塞队列时要尤其当心,当任务耗时较长时可能会导致大量新任务在队列中堆积最终导致OOM。最近工作中就遇到因为采用LinkedBlockingQueue作为阻塞队列,部分任务耗时80s+且不停有新任务进来,导致cpu和内存飙升服务器挂掉。
有界队列
常用的有两类,一类是遵循FIFO原则的队列如ArrayBlockingQueue与有界的LinkedBlockingQueue,另一类是优先级队列如PriorityBlockingQueue。PriorityBlockingQueue中的优先级由任务的Comparator决定。 
使用有界队列时队列大小需和线程池大小互相配合,线程池较小有界队列较大时可减少内存消耗,降低cpu使用率和上下文切换,但是可能会限制系统吞吐量。
同步移交
如果不希望任务在队列中等待而是希望将任务直接移交给工作线程,可使用SynchronousQueue作为等待队列。SynchronousQueue不是一个真正的队列,而是一种线程之间移交的机制。要将一个元素放入SynchronousQueue中,必须有另一个线程正在等待接收这个元素。只有在使用无界线程池或者有饱和策略时才建议使用该队列。
newCachedThreadPool
     public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
在newCachedThreadPool中如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。 
初看该构造函数时我有这样的疑惑:核心线程池为0,那按照前面所讲的线程池策略新任务来临时无法进入核心线程池,只能进入 SynchronousQueue中进行等待,而SynchronousQueue的大小为1,那岂不是第一个任务到达时只能等待在队列中,直到第二个任务到达发现无法进入队列才能创建第一个线程? 
这个问题的答案在上面讲SynchronousQueue时其实已经给出了,要将一个元素放入SynchronousQueue中,必须有另一个线程正在等待接收这个元素。因此即便SynchronousQueue一开始为空且大小为1,第一个任务也无法放入其中,因为没有线程在等待从SynchronousQueue中取走元素。因此第一个任务到达时便会创建一个新线程执行该任务。 
这里引申出一个小技巧:有时我们可能希望线程池在没有任务的情况下销毁所有的线程,既设置线程池核心大小为0,但又不想使用SynchronousQueue而是想使用有界的等待队列。显然,不进行任何特殊设置的话这样的用法会发生奇怪的行为:直到等待队列被填满才会有新线程被创建(ps 大于等待队列小于最大线程池),任务才开始执行。这并不是我们希望看到的,此时可通过allowCoreThreadTimeOut使等待队列中的元素出队被调用执行,详细原理和使用将会在后续博客中阐述。
newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行
newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
newWorkStealingPool创建一个拥有多个任务队列(以便减少连接数)的线程池
使用无界队列的线程池会导致内存飙升吗?
ps:以图文的形式讲解线程池的运行过程。OOM会发生在无界队列
当出现workQueue里不断的积压越来越多得任务,不停的增加。
这个过程中会导致机器的内存使用不停的飙升,最后也许极端情况下就导致JVM OOM了,系统就挂掉了。
阻塞队列中任务过多会导致OOM异常。
线程过多会导致上下文切换的开销、消耗cpu资源
实际上,CPU(中央处理器)使用抢占式调度模式在多个线程间进行着高速的切换。对于CPU的一个核而言,某个时刻,只能执行一个线程,而 CPU的在多个线程间切换速度相对我们的感觉要快,看上去就是在同一时刻运行。
其实,多线程程序并不能提高程序的运行速度,但能够提高程序运行效率,让CPU的使用率更高。
我们详细的解释一下为什么要使用线程池?
在java中,如果每个请求到达就创建一个新线程,开销是相当大的。在实际使用中,创建和销毁线程花费的时间和消耗的系统资源都相当大,甚至可能要比在处理实际的用户请求的时间和资源要多的多。除了创建和销毁线程的开销之外,活动的线程也需要消耗系统资源。如果在一个jvm里创建太多的线程,可能会使系统由于过度消耗内存或“切换过度”而导致系统资源不足。为了防止资源不足,需要采取一些办法来限制任何给定时刻处理的请求数目,尽可能减少创建和销毁线程的次数,特别是一些资源耗费比较大的线程的创建和销毁,尽量利用已有对象来进行服务。
线程池主要用来解决线程生命周期开销问题和资源不足问题。通过对多个任务重复使用线程,线程创建的开销就被分摊到了多个任务上了,而且由于在请求到达时线程已经存在,所以消除了线程创建所带来的延迟。这样,就可以立即为请求服务,使用应用程序响应更快。另外,通过适当的调整线程中的线程数目可以防止出现资源不足的情况。
java线程池与五种常用线程池策略使用与解析的更多相关文章
- java线程池和五种常用线程池的策略使用与解析
		java线程池和五种常用线程池策略使用与解析 一.线程池 关于为什么要使用线程池久不赘述了,首先看一下java中作为线程池Executor底层实现类的ThredPoolExecutor的构造函数 pu ... 
- PHP5 的五种常用模式
		PHP5 的五种常用模式. 工厂模式 最初在设计模式 一书中,许多设计模式都鼓励使用松散耦合.要理解这个概念,让我们最好谈一下许多开发人员从事大型系统的艰苦历程.在更改一个代码片段时,就会发生问题,系 ... 
- Spring Cloud Config采用Git存储时两种常用的配置策略
		由于Spring Cloud Config默认采用了Git存储,相信很多团队在使用Spring Cloud的配置中心时也会采用这样的策略.即便大家都使用了Git存储,可能还有各种不同的配置方式,本文就 ... 
- JAVA中创建线程池的五种方法及比较
		之前写过JAVA中创建线程的三种方法及比较.这次来说说线程池. JAVA中创建线程池主要有两类方法,一类是通过Executors工厂类提供的方法,该类提供了4种不同的线程池可供使用.另一类是通过Thr ... 
- java中4种常用线程池
		一.线程池 线程池:说白了,就是一种线程使用模式.线程过多会带来调度开销,进而影响整体性能.而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务,这避免了在处理短时间任务时创建与销毁线程的代价 ... 
- 高级java必会系列一:常用线程池和调度类
		众所周知,开启线程2种方法:第一是实现Runable接口,第二继承Thread类.(当然内部类也算...)常用的,这里就不再赘述. 一.线程池 1.newCachedThreadPool (1)缓存型 ... 
- 深入Java线程管理(五):线程池
		这几天主要是狂看源程序,在弥补了一些以前知识空白的同时,也学会了不少新的知识(比如 NIO),或者称为新技术吧. 线程池就是其中之一,一提到线程,我们会想到以前<操作系统>的生产者与消费者 ... 
- 【Java】设计模型-五种单例模型
		一. 什么是单例模式 只需要某个类同时保留一个对象,不希望有更多对象,此时,我们则应考虑单例模式的设计. 单例模式的主要作用是保证在Java程序中,某个类只有一个实例存在. 单例模式有很多好处,它能够 ... 
- Java多线程(1):3种常用的实现多线程类的方法
		(1) 继承java.lang.Thread类(Thread也实现了Runnable接口) 继承Thread类的方法是比较常用的一种,如果说你只是想起一条线程.没有什么其它特殊的要求,那么可以使用Th ... 
随机推荐
- 未能加载文件或程序集“System.Web.Mvc, Version=5.2.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35”或它的某一个依赖项
			楼主创建项目的时候选择的是5.2.4的版本,但是后来改成了5.0.0于是出现了这个错误,解决的方法倒也简单 将View文件夹下 web.config文件中 以下两处 版本改成当前版本就行了 
- redis -hash(哈希.对象)
			hash 用于储存对象,对象的结构为属性.值 值的类型string 增加.修改: 设置单个属性: hset 键 field 值 例如: 设置键 user 的属性name 为 python hset u ... 
- HDU 6152 - Friend-Graph
			Friend-Graph Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Tot ... 
- 关于Java 中跳出多重循环
			前言 环境:window10 JDK 1.8 应用场景:在多个for循环或while循环中,直接跳到最外层的循环外面,而不是需要层层退出来. 使用: 使用一个标签label(也可以是其他单词,不能是关 ... 
- Swift 产生 uuid
			项目中.需要客户端生成一个唯一的识别码 let uuid = UUID().uuidString print(uuid) 
- eclipse中使用Lombok(转)
			原文链接:https://www.cnblogs.com/justuntil/p/7120534.html windows环境 1.下载lombok.jar包https://projectlombok ... 
- java中的超类是什么
			超类(SuperClass) :用java术语来讲,被继承的类称为超类(SuperClass),也有叫做父类,继承的类称为子类. 
- Web 项目系列之浏览器机制(一)
			目录: ——初步认识浏览器 ——浏览器的渲染机制 ——浏览器的缓存机制 正文: 初步认识浏览器 想来任何一位读者,对浏览器都不会陌生.除开IT相关人员常用的Chrome(谷歌,Google).Fi ... 
- 【算法】螺旋方阵 上交OJ1021
			输入格式: 输入在一行中给出一个正整数N(<10). 输出格式: 输出N×N的螺旋方阵.每行N个数字,每个数字占3位. 输入样例: 5 1 2 3 4 5 16 17 18 19 6 15 24 ... 
- 软工+C(9): 助教指南,持续更新...
			上一篇:提问与回复 下一篇:从命令行开始逐步培养编程能力(Java) 目录: ** 0x00 Handshake ** 0x01 点评 ** 0x02 评分 ** 0x03 知识储备 ** 0x04 ... 
