Java并发编程:线程池的使用
2016-03-21 15:29
761 查看
Java并发编程:线程池的使用
在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题:
如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。
那么有没有一种办法使得线程可以复用,就是执行完一个任务,并不被销毁,而是可以继续执行其他的任务?
在Java中可以通过线程池来达到这样的效果。今天我们就来详细讲解一下Java的线程池,首先我们从最核心的ThreadPoolExecutor类中的方法讲起,然后再讲述它的实现原理,接着给出了它的使用示例,最后讨论了一下如何合理配置线程池的大小。
以下是本文的目录大纲:
一.Java中的ThreadPoolExecutor类
二.深入剖析线程池实现原理
三.使用示例
四.如何合理配置线程池的大小
若有不正之处请多多谅解,并欢迎批评指正。
请尊重作者劳动成果,转载请标明原文链接:
http://www.cnblogs.com/dolphin0520/p/3932921.html
在ThreadPoolExecutor类中提供了四个构造方法:
从上面的代码可以得知,ThreadPoolExecutor继承了AbstractExecutorService类,并提供了四个构造器,事实上,通过观察每个构造器的源码具体实现,发现前面三个构造器都是调用的第四个构造器进行的初始化工作。
下面解释下一下构造器中各个参数的含义:
corePoolSize:核心池的大小,这个参数跟后面讲述的线程池的实现原理有非常大的关系。在创建了线程池后,默认情况下,线程池中并没有任何线程,而是等待有任务到来才创建线程去执行任务,除非调用了prestartAllCoreThreads()或者prestartCoreThread()方法,从这2个方法的名字就可以看出,是预创建线程的意思,即在没有任务到来之前就创建corePoolSize个线程或者一个线程。默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中;
maximumPoolSize:线程池最大线程数,这个参数也是一个非常重要的参数,它表示在线程池中最多能创建多少个线程;
keepAliveTime:表示线程没有任务执行时最多保持多久时间会终止。默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用,直到线程池中的线程数不大于corePoolSize,即当线程池中的线程数大于corePoolSize时,如果一个线程空闲的时间达到keepAliveTime,则会终止,直到线程池中的线程数不超过corePoolSize。但是如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0;
unit:参数keepAliveTime的时间单位,有7种取值,在TimeUnit类中有7种静态属性:
从执行结果可以看出,当线程池中线程的数目大于5时,便将任务放入任务缓存队列里面,当任务缓存队列满了之后,便创建新的线程。如果上面程序中,将for循环中改成执行20个任务,就会抛出任务拒绝异常了。
不过在java doc中,并不提倡我们直接使用ThreadPoolExecutor,而是使用Executors类中提供的几个静态方法来创建线程池:
下面是这三个静态方法的具体实现;
从它们的具体实现来看,它们实际上也是调用了ThreadPoolExecutor,只不过参数都已配置好了。
newFixedThreadPool创建的线程池corePoolSize和maximumPoolSize值是相等的,它使用的LinkedBlockingQueue;
newSingleThreadExecutor将corePoolSize和maximumPoolSize都设置为1,也使用的LinkedBlockingQueue;
newCachedThreadPool将corePoolSize设置为0,将maximumPoolSize设置为Integer.MAX_VALUE,使用的SynchronousQueue,也就是说来了任务就创建线程运行,当线程空闲超过60秒,就销毁线程。
实际中,如果Executors提供的三个静态方法能满足要求,就尽量使用它提供的三个方法,因为自己去手动配置ThreadPoolExecutor的参数有点麻烦,要根据实际任务的类型和数量来进行配置。
另外,如果ThreadPoolExecutor达不到要求,可以自己继承ThreadPoolExecutor类进行重写。
一般需要根据任务的类型来配置线程池大小:
如果是CPU密集型任务,就需要尽量压榨CPU,参考值可以设为 NCPU+1
如果是IO密集型任务,参考值可以设置为2*NCPU
当然,这只是一个参考值,具体的设置还需要根据实际情况进行调整,比如可以先将线程池大小设置为参考值,再观察任务运行情况和系统负载、资源利用率来进行适当调整。
参考资料:
http://ifeve.com/java-threadpool/
http://blog.163.com/among_1985/blog/static/275005232012618849266/
http://developer.51cto.com/art/201203/321885.htm
/article/1599506.html
/article/1684689.html
/article/1388796.html
在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题:
如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。
那么有没有一种办法使得线程可以复用,就是执行完一个任务,并不被销毁,而是可以继续执行其他的任务?
在Java中可以通过线程池来达到这样的效果。今天我们就来详细讲解一下Java的线程池,首先我们从最核心的ThreadPoolExecutor类中的方法讲起,然后再讲述它的实现原理,接着给出了它的使用示例,最后讨论了一下如何合理配置线程池的大小。
以下是本文的目录大纲:
一.Java中的ThreadPoolExecutor类
二.深入剖析线程池实现原理
三.使用示例
四.如何合理配置线程池的大小
若有不正之处请多多谅解,并欢迎批评指正。
请尊重作者劳动成果,转载请标明原文链接:
http://www.cnblogs.com/dolphin0520/p/3932921.html
一.Java中的ThreadPoolExecutor类
java.uitl.concurrent.ThreadPoolExecutor类是线程池中最核心的一个类,因此如果要透彻地了解Java中的线程池,必须先了解这个类。下面我们来看一下ThreadPoolExecutor类的具体实现源码。在ThreadPoolExecutor类中提供了四个构造方法:
下面解释下一下构造器中各个参数的含义:
corePoolSize:核心池的大小,这个参数跟后面讲述的线程池的实现原理有非常大的关系。在创建了线程池后,默认情况下,线程池中并没有任何线程,而是等待有任务到来才创建线程去执行任务,除非调用了prestartAllCoreThreads()或者prestartCoreThread()方法,从这2个方法的名字就可以看出,是预创建线程的意思,即在没有任务到来之前就创建corePoolSize个线程或者一个线程。默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中;
maximumPoolSize:线程池最大线程数,这个参数也是一个非常重要的参数,它表示在线程池中最多能创建多少个线程;
keepAliveTime:表示线程没有任务执行时最多保持多久时间会终止。默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用,直到线程池中的线程数不大于corePoolSize,即当线程池中的线程数大于corePoolSize时,如果一个线程空闲的时间达到keepAliveTime,则会终止,直到线程池中的线程数不超过corePoolSize。但是如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0;
unit:参数keepAliveTime的时间单位,有7种取值,在TimeUnit类中有7种静态属性:
正在执行task 0 线程池中线程数目:1,队列中等待执行的任务数目:0,已执行玩别的任务数目:0 线程池中线程数目:2,队列中等待执行的任务数目:0,已执行玩别的任务数目:0 正在执行task 1 线程池中线程数目:3,队列中等待执行的任务数目:0,已执行玩别的任务数目:0 正在执行task 2 线程池中线程数目:4,队列中等待执行的任务数目:0,已执行玩别的任务数目:0 正在执行task 3 线程池中线程数目:5,队列中等待执行的任务数目:0,已执行玩别的任务数目:0 正在执行task 4 线程池中线程数目:5,队列中等待执行的任务数目:1,已执行玩别的任务数目:0 线程池中线程数目:5,队列中等待执行的任务数目:2,已执行玩别的任务数目:0 线程池中线程数目:5,队列中等待执行的任务数目:3,已执行玩别的任务数目:0 线程池中线程数目:5,队列中等待执行的任务数目:4,已执行玩别的任务数目:0 线程池中线程数目:5,队列中等待执行的任务数目:5,已执行玩别的任务数目:0 线程池中线程数目:6,队列中等待执行的任务数目:5,已执行玩别的任务数目:0 正在执行task 10 线程池中线程数目:7,队列中等待执行的任务数目:5,已执行玩别的任务数目:0 正在执行task 11 线程池中线程数目:8,队列中等待执行的任务数目:5,已执行玩别的任务数目:0 正在执行task 12 线程池中线程数目:9,队列中等待执行的任务数目:5,已执行玩别的任务数目:0 正在执行task 13 线程池中线程数目:10,队列中等待执行的任务数目:5,已执行玩别的任务数目:0 正在执行task 14 task 3执行完毕 task 0执行完毕 task 2执行完毕 task 1执行完毕 正在执行task 8 正在执行task 7 正在执行task 6 正在执行task 5 task 4执行完毕 task 10执行完毕 task 11执行完毕 task 13执行完毕 task 12执行完毕 正在执行task 9 task 14执行完毕 task 8执行完毕 task 5执行完毕 task 7执行完毕 task 6执行完毕 task 9执行完毕
从执行结果可以看出,当线程池中线程的数目大于5时,便将任务放入任务缓存队列里面,当任务缓存队列满了之后,便创建新的线程。如果上面程序中,将for循环中改成执行20个任务,就会抛出任务拒绝异常了。
不过在java doc中,并不提倡我们直接使用ThreadPoolExecutor,而是使用Executors类中提供的几个静态方法来创建线程池:
newFixedThreadPool创建的线程池corePoolSize和maximumPoolSize值是相等的,它使用的LinkedBlockingQueue;
newSingleThreadExecutor将corePoolSize和maximumPoolSize都设置为1,也使用的LinkedBlockingQueue;
newCachedThreadPool将corePoolSize设置为0,将maximumPoolSize设置为Integer.MAX_VALUE,使用的SynchronousQueue,也就是说来了任务就创建线程运行,当线程空闲超过60秒,就销毁线程。
实际中,如果Executors提供的三个静态方法能满足要求,就尽量使用它提供的三个方法,因为自己去手动配置ThreadPoolExecutor的参数有点麻烦,要根据实际任务的类型和数量来进行配置。
另外,如果ThreadPoolExecutor达不到要求,可以自己继承ThreadPoolExecutor类进行重写。
四.如何合理配置线程池的大小
本节来讨论一个比较重要的话题:如何合理配置线程池大小,仅供参考。一般需要根据任务的类型来配置线程池大小:
如果是CPU密集型任务,就需要尽量压榨CPU,参考值可以设为 NCPU+1
如果是IO密集型任务,参考值可以设置为2*NCPU
当然,这只是一个参考值,具体的设置还需要根据实际情况进行调整,比如可以先将线程池大小设置为参考值,再观察任务运行情况和系统负载、资源利用率来进行适当调整。
参考资料:
http://ifeve.com/java-threadpool/
http://blog.163.com/among_1985/blog/static/275005232012618849266/
http://developer.51cto.com/art/201203/321885.htm
/article/1599506.html
/article/1684689.html
/article/1388796.html
相关文章推荐
- LinkedHashMap超详细分析
- java中String字符串的替换函数:replace与replaceAll的区别
- Java枚举常见7种用法
- JAVA中string.replace()和string.replaceAll()的区别及用
- Java RMI 简单示例
- 手把手教你整合 SpringMvc+Spring+MyBatis+Maven
- Struts 2 + Hibernate + Spring 整合要点
- Struts2 入门实例
- java 学习笔记
- Spring MVC 实践笔记
- Java数据库
- 关于java中Double类型的运算精度问题(转)
- 数组复制引用问题
- java基础经典练习题
- java线程研究
- 搭建SSH框架遇到的struts.xml找不到spring定义的bean的错误(配置文件的路径问题)
- Java中的时间表示和日期的转换
- Java String.replace()方法
- 圣思源-张龙-Java SE 第五讲(运算符续 Operator cont.).pdf
- java.net.ConnectException: Connection timed out