- 浏览: 80927 次
- 性别:
- 来自: 北京
http://daoger.iteye.com/blog/142485
背景
前段时间一个项目中因为涉及大量的线程开发,把jdk cocurrent的代码重新再过了一遍。这篇文章中主要是记录一下学习ThreadPoolExecutor过程中容易被人忽略的点,Doug Lea的整个类设计还是非常nice的
正文
先看一副图,描述了ThreadPoolExecutor的工作机制:
整个ThreadPoolExecutor的任务处理有4步操作:
第一步,初始的poolSize < corePoolSize,提交的runnable任务,会直接做为new一个Thread的参数,立马执行
第二步,当提交的任务数超过了corePoolSize,就进入了第二步操作。会将当前的runable提交到一个block queue中
第三步,如果block queue是个有界队列,当队列满了之后就进入了第三步。如果poolSize < maximumPoolsize时,会尝试new 一个Thread的进行救急处理,立马执行对应的runnable任务
第四步,如果第三步救急方案也无法处理了,就会走到第四步执行reject操作。
几点说明:(相信这些网上一搜一大把,我这里简单介绍下,为后面做一下铺垫)
block queue有以下几种实现:
1. ArrayBlockingQueue : 有界的数组队列
2. LinkedBlockingQueue : 可支持有界/无界的队列,使用链表实现
3. PriorityBlockingQueue : 优先队列,可以针对任务排序
4. SynchronousQueue : 队列长度为1的队列,和Array有点区别就是:client thread提交到block queue会是一个阻塞过程,直到有一个worker thread连接上来poll task。
RejectExecutionHandler是针对任务无法处理时的一些自保护处理:
1. Reject 直接抛出Reject exception
2. Discard 直接忽略该runnable,不可取
3. DiscardOldest 丢弃最早入队列的的任务
4. CallsRun 直接让原先的client thread做为worker线程,进行执行
容易被人忽略的点:
1. pool threads启动后,以后的任务获取都会通过block queue中,获取堆积的runnable task.
所以建议: block size >= corePoolSize ,不然线程池就没任何意义
2. corePoolSize 和 maximumPoolSize的区别, 和大家正常理解的数据库连接池不太一样。
* 据dbcp pool为例,会有minIdle , maxActive配置。minIdle代表是常驻内存中的threads数量,maxActive代表是工作的最大线程数。
* 这里的corePoolSize就是连接池的maxActive的概念,它没有minIdle的概念(每个线程可以设置keepAliveTime,超过多少时间多有任务后销毁线程,但不会固定保持一定数量的threads)。
* 这里的maximumPoolSize,是一种救急措施的第一层。当threadPoolExecutor的工作threads存在满负荷,并且block queue队列也满了,这时代表接近崩溃边缘。这时允许临时起一批threads,用来处理runnable,处理完后立马退出。
所以建议: maximumPoolSize >= corePoolSize =期望的最大线程数。 (我曾经配置了corePoolSize=1, maximumPoolSize=20, blockqueue为无界队列,最后就成了单线程工作的pool。典型的配置错误)
3. 善用blockqueue和reject组合. 这里要重点推荐下CallsRun的Rejected Handler,从字面意思就是让调用者自己来运行。
我们经常会在线上使用一些线程池做异步处理,比如我前面做的(业务层)异步并行加载技术分析和设计, 将原本串行的请求都变为了并行操作,但过多的并行会增加系统的负载(比如软中断,上下文切换)。所以肯定需要对线程池做一个size限制。但是为了引入异步操作后,避免因在block queue的等待时间过长,所以需要在队列满的时,执行一个callsRun的策略,并行的操作又转为一个串行处理,这样就可以保证尽量少的延迟影响。
所以建议: RejectExecutionHandler = CallsRun , blockqueue size = 2 * poolSize (为啥是2倍poolSize,主要一个考虑就是瞬间高峰处理,允许一个thread等待一个runnable任务)
背景
前段时间一个项目中因为涉及大量的线程开发,把jdk cocurrent的代码重新再过了一遍。这篇文章中主要是记录一下学习ThreadPoolExecutor过程中容易被人忽略的点,Doug Lea的整个类设计还是非常nice的
正文
先看一副图,描述了ThreadPoolExecutor的工作机制:
整个ThreadPoolExecutor的任务处理有4步操作:
第一步,初始的poolSize < corePoolSize,提交的runnable任务,会直接做为new一个Thread的参数,立马执行
第二步,当提交的任务数超过了corePoolSize,就进入了第二步操作。会将当前的runable提交到一个block queue中
第三步,如果block queue是个有界队列,当队列满了之后就进入了第三步。如果poolSize < maximumPoolsize时,会尝试new 一个Thread的进行救急处理,立马执行对应的runnable任务
第四步,如果第三步救急方案也无法处理了,就会走到第四步执行reject操作。
几点说明:(相信这些网上一搜一大把,我这里简单介绍下,为后面做一下铺垫)
block queue有以下几种实现:
1. ArrayBlockingQueue : 有界的数组队列
2. LinkedBlockingQueue : 可支持有界/无界的队列,使用链表实现
3. PriorityBlockingQueue : 优先队列,可以针对任务排序
4. SynchronousQueue : 队列长度为1的队列,和Array有点区别就是:client thread提交到block queue会是一个阻塞过程,直到有一个worker thread连接上来poll task。
RejectExecutionHandler是针对任务无法处理时的一些自保护处理:
1. Reject 直接抛出Reject exception
2. Discard 直接忽略该runnable,不可取
3. DiscardOldest 丢弃最早入队列的的任务
4. CallsRun 直接让原先的client thread做为worker线程,进行执行
容易被人忽略的点:
1. pool threads启动后,以后的任务获取都会通过block queue中,获取堆积的runnable task.
所以建议: block size >= corePoolSize ,不然线程池就没任何意义
2. corePoolSize 和 maximumPoolSize的区别, 和大家正常理解的数据库连接池不太一样。
* 据dbcp pool为例,会有minIdle , maxActive配置。minIdle代表是常驻内存中的threads数量,maxActive代表是工作的最大线程数。
* 这里的corePoolSize就是连接池的maxActive的概念,它没有minIdle的概念(每个线程可以设置keepAliveTime,超过多少时间多有任务后销毁线程,但不会固定保持一定数量的threads)。
* 这里的maximumPoolSize,是一种救急措施的第一层。当threadPoolExecutor的工作threads存在满负荷,并且block queue队列也满了,这时代表接近崩溃边缘。这时允许临时起一批threads,用来处理runnable,处理完后立马退出。
所以建议: maximumPoolSize >= corePoolSize =期望的最大线程数。 (我曾经配置了corePoolSize=1, maximumPoolSize=20, blockqueue为无界队列,最后就成了单线程工作的pool。典型的配置错误)
3. 善用blockqueue和reject组合. 这里要重点推荐下CallsRun的Rejected Handler,从字面意思就是让调用者自己来运行。
我们经常会在线上使用一些线程池做异步处理,比如我前面做的(业务层)异步并行加载技术分析和设计, 将原本串行的请求都变为了并行操作,但过多的并行会增加系统的负载(比如软中断,上下文切换)。所以肯定需要对线程池做一个size限制。但是为了引入异步操作后,避免因在block queue的等待时间过长,所以需要在队列满的时,执行一个callsRun的策略,并行的操作又转为一个串行处理,这样就可以保证尽量少的延迟影响。
所以建议: RejectExecutionHandler = CallsRun , blockqueue size = 2 * poolSize (为啥是2倍poolSize,主要一个考虑就是瞬间高峰处理,允许一个thread等待一个runnable任务)
发表评论
-
组合or继承
2013-05-27 11:54 822到底使用组合还是继承是每本讲设计的资料里都要讨论一番的话题 ... -
Java访问控制private之我见
2013-05-24 11:36 783最近待业在家,遂有空重新读了thinking in Java这 ... -
XML 系列教程
2012-05-06 12:50 586http://www.w3school.com.cn/x.as ... -
面向接口编程详解
2012-04-19 21:42 1107面向接口编程详解 2009-04-23 作者:张洋 来源: ... -
浅析java回调机制与观察者模式
2012-04-10 17:23 17891 java回调机制: 首先解 ... -
Java程序设计之-复合优先于继承
2012-04-03 10:33 1443组合 通过创建一个由其他对象组合的对象来获得新功能的重用方法 ... -
java学习之路(转)
2012-03-30 15:01 761(一) 从事软件 ... -
java内部类
2012-03-28 16:26 854一、 定义 放在一个类的内部的类我们就叫内部类。 二、 作用 ... -
为什么匿名内部类只能访问其所在方法中的final变量(转)
2012-03-28 15:45 1030(1).所谓“局部内部类”就是在对象的方法成员内部定义的类。而 ... -
Java访问权限修饰符(转)
2012-03-28 11:20 10611、Class类的访问权限: ... -
【java】好书推荐
2012-03-26 15:31 1439Java软件架构师所要需的东西 作为Java程序员来说,最痛 ... -
Java绝对好文,转载的!(转载)
2012-03-25 14:45 791想来学习Java也有两个年头了,永远不敢说多么精通,但也想谈谈 ... -
理解java动态加载机制
2012-03-20 00:01 10071.java动态性 java是一种 ... -
热部署、热加载
2012-03-19 14:14 3659不重启Tomcat有两种方式:热部署、热加载 热部署:容 ... -
Registry of Singleton 模式(转)
2012-03-06 10:01 764考虑使用 Singleton 模式 时拥有子类别的问题,在Si ... -
单例模式(Singleton Pattern)
2012-03-05 20:40 6806.单例模式(Singleton Pattern) 前面说提到 ... -
线程----BlockingQueue (转),java
2012-02-26 13:50 792/** 本例介绍一个 ... -
关于多个线程同时调用单例模式的对象,该对象中方法的局部变量是否会受多个线程的影响
2012-02-12 12:16 2905对于那些会以多线程运行的单例类,例如Web应用中的Servle ... -
Java线程同步机制synchronized关键字的理解
2011-12-25 14:34 734由于同一进程的多个线 ... -
synchronized与static synchronized 的区别
2011-12-24 22:48 6491.synchronized与static synchroni ...
相关推荐
java.util.concurrent-多线程框架
不客气地说,创建 java.util.concurrent 的目的就是要实现 Collection 框架对数据结构所执行的并发操作。通过提供一组可靠的、高性能并发构建块,开发人员可以提高并发类的线程安全、可伸缩性、性能、可读性和可靠性...
java.util.concurrent.atomic 类的小工具包,支持在单个变量上解除锁的线程安全编程。 java.util.concurrent.locks 为锁和等待条件提供一个框架的接口和类,它不同于内置同步和监视器。 java.util.jar 提供读写 ...
无论是工作学习,不断的总结是必不可少的。只有不断的总结,发现问题,弥补不足,才能长久的...java.util.concurrent.locks包下常用的类 326 NIO(New IO) 327 volatile详解 337 Java 8新特性 347 Java 性能优化 362
集合可以看作是一种容器,用来存储对象信息。所有集合类都位于java.util包下,但支持多线程的集合类位于java.util.concurrent包下。
java.util.concurrent包、synchronized关键字、Swing框架、Java内存模型等内容也均有涉及,不仅能够了解Java多线程的相关知识,还可加深对Java语言的理解。 本书适合以下读者阅读 a.对多线程感兴趣的人 b.对Java...
java.util.concurrent.atomic 类的小工具包,支持在单个变量上解除锁的线程安全编程。 java.util.concurrent.locks 为锁和等待条件提供一个框架的接口和类,它不同于内置同步和监视器。 java.util.jar 提供读写 ...
java.util.concurrent.atomic 类的小工具包,支持在单个变量上解除锁的线程安全编程。 java.util.concurrent.locks 为锁和等待条件提供一个框架的接口和类,它不同于内置同步和监视器。 java.util.jar 提供读写 ...
java.util.concurrent.atomic 类的小工具包,支持在单个变量上解除锁的线程安全编程。 java.util.concurrent.locks 为锁和等待条件提供一个框架的接口和类,它不同于内置同步和监视器。 java.util.jar 提供读写 JAR ...
java.util.concurrent.atomic 类的小工具包,支持在单个变量上解除锁的线程安全编程。 java.util.concurrent.locks 为锁和等待条件提供一个框架的接口和类,它不同于内置同步和监视器。 java.util.jar 提供读写 JAR ...
java.util.concurrent.atomic 类的小工具包,支持在单个变量上解除锁的线程安全编程。 java.util.concurrent.locks 为锁和等待条件提供一个框架的接口和类,它不同于内置同步和监视器。 java.util.jar 提供读写 JAR ...
java.util.concurrent.atomic 类的小工具包,支持在单个变量上解除锁的线程安全编程。 java.util.concurrent.locks 为锁和等待条件提供一个框架的接口和类,它不同于内置同步和监视器。 java.util.jar 提供读写 ...
第2章介绍了 Java 并行程序开发的基础, 包括 Java 中 Thread 的基本使用方法等第3章介绍了 JDK 内部对并行程序开发的支持, 主要介绍 JUC (Java.util.concurrent) 中些工具的使用方法、 各自特点及它们的内部实现...
java.util.concurrent.atomic 类的小工具包,支持在单个变量上解除锁的线程安全编程。 java.util.concurrent.locks 为锁和等待条件提供一个框架的接口和类,它不同于内置同步和监视器。 java.util.jar 提供读写 ...
多线程和并发: Java支持多线程编程,允许同时执行多个任务。Java中的并发编程机制包括线程、同步、锁等,以及java.util.concurrent包提供的高级并发功能。 异常处理: Java通过异常处理机制提供了一种结构化的错误...
java.util.concurrent.atomic 类的小工具包,支持在单个变量上解除锁的线程安全编程。 java.util.concurrent.locks 为锁和等待条件提供一个框架的接口和类,它不同于内置同步和监视器。 java.util.jar 提供读写 JAR...
Java 并发编程中的 JUC(java.util.concurrent)库以及其核心组件 AQS(AbstractQueuedSynchronizer)在构建高性能、可伸缩性的多线程应用方面具有重要的地位。 AQS 是 JUC 中的核心组件,它提供了一个框架,让...