线程池应对于突然增大、来不及处理的请求,无非两种应对方式:

  1. 将未完成的请求放在队列里等待
  2. 临时增加处理线程,等高峰回落后再结束临时线程

JDK的Executors.newFixedPool() 和newCachedPool(),分别使用了这两种方式。

不过,这俩函数在方便之余,也屏蔽了ThreadPool原本多样的配置,对一些不求甚解的码农来说,就错过了一些更适合自己项目的选择。

1. ThreadPoolExecutor的原理

经典书《Java Concurrency in Pratice(Java并发编程实战)》的第8章,浓缩如下:

1. 每次提交任务时,如果线程数还没达到coreSize就创建新线程并绑定该任务。

所以第coreSize次提交任务后线程总数必达到coreSize,不会重用之前的空闲线程。

在生产环境,为了避免首次调用超时,可以调用executor.prestartCoreThread()预创建所有core线程,避免来一个创一个带来首次调用慢的问题。

2. 线程数达到coreSize后,新增的任务就放到工作队列里,而线程池里的线程则努力的使用take()阻塞地从工作队列里拉活来干。

3. 如果队列是个有界队列,又如果线程池里的线程不能及时将任务取走,工作队列可能会满掉,插入任务就会失败,此时线程池就会紧急的再创建新的临时线程来补救。

4. 临时线程使用poll(keepAliveTime,timeUnit)来从工作队列拉活,如果时候到了仍然两手空空没拉到活,表明它太闲了,就会被解雇掉。

5. 如果core线程数+临时线程数 >maxSize,则不能再创建新的临时线程了,转头执行RejectExecutionHanlder。默认的AbortPolicy抛RejectedExecutionException异常,其他选择包括静默放弃当前任务(Discard),放弃工作队列里最老的任务(DisacardOldest),或由主线程来直接执行(CallerRuns),或你自己发挥想象力写的一个。 

2. FixedPool 与 CachedPool

FixedPool默认用了一条无有暗香盈袖界的工作队列 LinkedBlockingQueue, 所以只去到上面的第2步就不会继续往下走了,coreSize的线程做不完的任务不断堆积到无限长的Queue中。

所以只有coreSize一个参数,其他maxSize,keepAliveTime,RejectHandler的配置都不会实际生效。

CachedPool则把coreSize设成0,然后选用了一种特殊的Queue -- SynchronousQueue,只要当前没有空闲线程,Queue就会立刻报插入失败,让线程池增加新的临时线程,默认的KeepAliveTime是1分钟,而且maxSize是整型的最大值,也就是说只要有干不完的活,都会无限增增加线程数,直到高峰过去线程数才会回落。

3. 对FixedPool的进一步配置

3.1 设置QueueSize

如果不想搞一条无限长的Queue,避免任务无限等待显得像假死,同时占用太多内存,可能会把它换成一条有界的ArrayBlockingQueue,那就要同时关注一下这条队列满了之后的场景,选择正确的rejectHanlder。

此时,最好还是把maxSize设为coreSize一样的值,不把临时线程及其keepAlive时间拉进来,Queue+临时线程两者结合听是好听,但很难设置好。

3.2 有界队列选LinkedBlockingQueue 还是ArrayBlockingQueue?

按Executors的JavaDoc上说是ArrayBlockingQueue,起码ArrayBlockingQueue每插入一个Runnable就直接放到内部的数组里,而LinkedBlockingQueue则要 new Node(runnable),无疑会产生更多对象。而性能方面有兴趣的同学可以自己测一下。

allowCoreThreadTimeOut(true)

允许core线程也在完全没流量时收缩到0,但因为JDK的算法,只要当前线程数低于core,请求一来就会创建线程,不管现在有没有空闲的线程能服务这个请求,所以这个选项的作用有限,仅在完全没流量时有效。 但都完全没流量了,怎么滴其实也没所谓了。除非是同时有很多个线程池的情况。

4. 对CachedPool的进一步配置

4.1 设置coreSize

coreSize默认为0,但很多时候也希望是一个类似FixedPool的固定值,能处理大部分的情况,不要有太多加加减减的波动,等待和消耗的精力。

4.2 设置maxSize及rejectHandler

同理,maxSize默认是整形最大值,但太多的线程也很可能让系统崩溃,所以建议还是设一下maxSize和rejectHandler。

4.3 设置keepAliveTime

默认1分钟,可以根据项目再设置一把。

4.4 SynchronousQueue的性能?

高并发下,SynchronousQueue的性能绝对比LinkedBlockingQueue/ArrayBlockingQueue低一大截。虽然JDK6的实现号称比JDK5的改进很多,但还是慢,据文章说只在20线程并发下它才是快的。

5. SpringSide的ThreadPoolBuilder

广告时间,SpringSide的ThreadPoolBuilder能简化上述的配置。

此文太科普太水,主要就是为了帮SpringSide-Utils项目打广告:)

Java ThreadPool的正确打开方式花钱的年华 | 江南白衣(5星推荐)的更多相关文章

  1. C#语法——泛型的多种应用 C#语法——await与async的正确打开方式 C#线程安全使用(五) C#语法——元组类型 好好耕耘 redis和memcached的区别

    C#语法——泛型的多种应用   本篇文章主要介绍泛型的应用. 泛型是.NET Framework 2.0 版类库就已经提供的语法,主要用于提高代码的可重用性.类型安全性和效率. 泛型的定义 下面定义了 ...

  2. List的remove()方法的三种正确打开方式

    转: java编程:List的remove()方法的三种正确打开方式! 2018年08月12日 16:26:13 Aries9986 阅读数 2728更多 分类专栏: leetcode刷题   版权声 ...

  3. iOS开发小技巧--相机相册的正确打开方式

    iOS相机相册的正确打开方式- UIImagePickerController 通过指定sourceType来实现打开相册还是相机 UIImagePickerControllerSourceTypeP ...

  4. Xcode 的正确打开方式——Debugging(转载)

    Xcode 的正确打开方式——Debugging   程序员日常开发中有大量时间都会花费在 debug 上,从事 iOS 开发不可避免地需要使用 Xcode.这篇博客就主要介绍了 Xcode 中几种能 ...

  5. InnoDB缓冲池预加载在MySQL 5.7中的正确打开方式

    InnoDB缓冲池预加载在MySQL 5.7中的正确打开方式 https://mp.weixin.qq.com/s/HGa_90XvC22anabiBF8AbQ 在这篇文章里,我将讨论在MySQL 5 ...

  6. Console控制台的正确打开方式

    Console控制台的正确打开方式 console对象提供了访问浏览器调试模式的信息到控制台 -- Console对象 |-- assert() 如果第一个参数断言为false,则在控制台输出错误信息 ...

  7. 任务队列和异步接口的正确打开方式(.NET Core版本)

    任务队列和异步接口的正确打开方式 什么是异步接口? Asynchronous Operations Certain types of operations might require processi ...

  8. (一)Redis for Windows正确打开方式

    目录 (一)Redis for Windows正确打开方式 (二)Redis for 阿里云公网连接 (三)Redis for StackExchange.Redis 下载地址 官网.中文网1 及 中 ...

  9. C++11随机数的正确打开方式

    C++11随机数的正确打开方式 在C++11之前,现有的随机数函数都存在一个问题:在利用循环多次获取随机数时,如果程序运行过快或者使用了多线程等方法,srand((unsigned)time(null ...

随机推荐

  1. 潭州课堂25班:Ph201805201 第五课:格式化输出和深浅复制 (课堂笔记)

    格式化输出和字符串转义 占位符 使用示意 作用 %s '%s %s' % ('hello', 'world') 表示占位的是str %d '%d %d' % (1, 2) 表示占位的是int %d ' ...

  2. 构造函数,super()

    一.构造函数的作用 创建类的对象,只有对象才能调用一个类中的方法和属性 二.在有继承关系的类中,创建子类对象,初始化的顺序 先父类构造 —>后子类构造 为什么父类构造会被调用?因为有继承关系,子 ...

  3. 【翻译】TCP backlog在Linux中的工作原理

    原文How TCP backlog works in Linux水平有限,难免有错,欢迎指出!以下为翻译: 当应用程序通过系统调用listen将一个套接字(socket)置为LISTEN状态时,需要为 ...

  4. 【转载】IntelliJ IDEA 内存优化最佳实践

    本文转自 http://blog.oneapm.com/apm-tech/426.html [编者按]本文作者在和同事的一次讨论中发现,对 IntelliJ IDEA 内存采用不同的设置方案,会对 I ...

  5. Web App 和 Native App,哪个是趋势?

    一.Web App vs. Native App 比起手机App,网站有一些明显的优点. 跨平台:所有系统都能运行 免安装:打开浏览器,就能使用 快速部署:升级只需在服务器更新代码 超链接:可以与其他 ...

  6. Git -- 本地 一个相同的新的分支 并 推送到远程仓库

    (一).创建本地分支 git checkout -b 新分支名 执行该指令后,会在本地创建一个新分支,该分支是从当前分支上检出的,所以所有文件内容都和当前分支一模一样,这是正常的.创建成功后,将自动切 ...

  7. 关于VC预定义常量_WIN32,WIN32,_WIN64(转)

    VC2012 下写 Windows 程序时,有时需要判断编译环境.在之前的文章<判断程序是否运行在 Windows x64 系统下.>里说过如何在运行期间判断系统环境,但在编译时如何判断? ...

  8. 如何将 Java 项目转换成 Maven 项目

    本文内容 Java 项目 Maven 项目 Java 项目转换成 Maven 项目 本文主要介绍如何将 Java 项目转换成 Maven 项目.首先要明确的是,用 Maven 管理 Java 项目的确 ...

  9. JNI学习小结

    Java中类型与C/C++中对应关系   Java中的类的对应   Sign签名, 用来识别对应各个方法. JDK下的javap.exe能输出签名.用法javap -s -p 完整类名     下面是 ...

  10. 外网IP监测上报程序(使用Poco库的SMTPClientSession发送邮件)

    目录 IPReport 项目介绍 编译说明 安装使用说明 获取外网IP方式 邮件发送关键代码 IPReport 代码地址https://gitee.com/solym/IPReport 项目介绍 外网 ...