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

  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. Log4j教程

    Log4j教程 一.快速上手 log4j把日志分为:all,trace,debug,info,warnig,error,fital,off等几个级别.级别高的会屏蔽级别的的信息. 1)maven导入j ...

  2. 弗洛伊德算法Floyed(求各顶点间最短路径):可打印最短路径

    #include <iostream> #include <string> #include <iomanip> using namespace std; #def ...

  3. Java内存区域与内存溢出异常--HotSpot虚拟机对象探秘

    以常用的HotSpot和常用的Java堆为例,深入探讨HotSpot虚拟机在Java堆中对象分配.布局和访问的全过程 1.对象的创建 ①虚拟机遇到一条new指令后,首先将去检查这个指令的参数是否能够在 ...

  4. putty失活不挂起运行

    https://blog.csdn.net/c1481118216/article/details/53010963 以下方式是试过了https://www.cnblogs.com/mysqlplus ...

  5. Delphi创建ActiveX控件,实现安全接口及无界面代码

    Delphi创建OCX控件非常的方便,但IE调用时弹出的安全认证非常麻烦,有时OCX也不需要界面,IE调用时需要隐藏,非常不方便.在DELPHI中创建OCX实现安全接口和创建事件中修改部分代码 实现安 ...

  6. shell命令行执行python(解析json)

    每个脚本都有自己的擅长. 有次实现一个work,使用了shell,php,python看着文件种类多,不方便交接,看着也比较麻烦. 减少文件种类数,也是很有必要的. 遇到的场景:shell程序需要从j ...

  7. 细说firewalld和iptables

    在RHEL7里有几种防火墙共存:firewalld.iptables.ebtables,默认是使用firewalld来管理netfilter子系统,不过底层调用的命令仍然是iptables等. fir ...

  8. WPF腾讯视频通话开发

    一.IntPtr.HandleC#中的IntPtr类型称为“平台特定的整数类型”,它们用于本机资源,如窗口句柄. 1.WPF窗口句柄IntPtr wnip = new System.Windows.I ...

  9. Linux 下Tomcat配置远程访问管理端

    1:修改Tomcat默认端口号,将默认的8080修改为8081 apache-tomcat-8.5.31\conf\server.xml <Connector port="8081&q ...

  10. 手把手教你用CAB发布OCX的简单办法

    CAB是一种文件压缩格式,把OCX文件(ActiveX插件)压缩成CAB文件是为了发布它,这种发布方式给人感觉“浏览器会自动安装该插件”,很多朋友钟情于此.目前流行很多压缩工具,但WINRAR这类工具 ...