您的位置:首页 > 编程语言 > Java开发

java自带线程池的使用

2017-06-17 11:52 295 查看

线程池简介

线程池技术一是用以控制系统中线程的数量,使得线程数不因太少而浪费资源,不因太多而导致效率不高(线程的创建和销毁都需要占用时间、资源)。

jdk1.4之前的版本中,线程池都极其简陋,jdk1.5之后引入了java.util.current之后,情况开始大幅改观。为我们解决线程问题提供了很大的便利。

java中线程池的接口

最顶级的接口是Excutors,但严格意义上说,Excutors并不是线程池,只是线程执行的工具,真正的线程池接口是ExcutorService。我们列出jdk中常用的一些有关线程池的类

1. newSingleThreadExecutor

创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。

2. newFixedThreadPool

创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。

3. newCachedThreadPool

创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,

那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。

4. newScheduledThreadPool

创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。

ThreadPoolExcutor详解

ThreadPoolExcutor的构造方法的完整参数是

ThreadPoolExcutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workingQueue,ThreadFactory threadFactory,RejectedExcutionHandler handler)
/**
*corePoolSize 线程池中保留的线程,包括闲置线程
*maximumPoolSize 线程池中允许的最大线程数
*keepAliveTime 当线程数大于核心数时,这是空闲线程等待新任务的最大时间
*unit -keepAliveTime的时间单位
*workingQueue 执行前用于保持任务的队列,此队列仅保持由excute()提交的Runnable任务
*threadFactory 执行程序创建新线程的工厂
*handler 由于超出线程范围和队列容量而使执行被阻塞时所使用的处理程序
*/


ThreadPoolExcutor是Excutors类的底层实现。

下面看下各类关于它的具体实现

public static ExcutorService newFixedThreadPool(int nThreads){
return new ThreadPoolExcutor(nThreads,nThreads,
0L,TimeUnit.MILLIONSECONDS,
new LinkedBlockingQueue<Runnable>);
}


在这里,corePoolSize和maximumPoolSize相等,keepAliveTime为0,LinkedBlockingQueue是无界的。

而单线程池

public static ExcutorService newSingleThreadExcutor(){
return new ThreadPoolExcutor(1,1,
0L,TimeUnit.MILLIONSECONDS,
new LinkedBlockingQueue<Runnable>);
}


newCachedThreadPool的实现

public static ExcutorService newCachedThreadPool(){
return new ThreadPoolExcutor(0,Integer.MAX_VALUE,
60L,TimeUnit.MILLIONSECONDS,
new SynchronousQueue<Runnable>)
}


关于BlockingQueue

所有的BlockingQueue都可以用于传输和保持提交的任务,可以使用此队列和池大小进行交互:

如果运行的线程少于线程池的corePoolSize,那么Excutor始终优先选择添加新的线程,而不进行排队;

如果线程数大于或者等于线程池的corePoolSize,那么优先选择进入队列,而不添加线程;

如果无法请求加入队列,则创建新的线程,除非线程数超过maximumPoolSize,这种情况下,任务会被拒绝。

排队的策略

直接提交:任务队列的默认选项是SynchronousQueue,它将任务直接提交给线程而不是保持它们。因此,如果不存在直接运行任务的线程,则试图将任务加入队列失效,因此会创建一个新的线程。此策略可以避免在处理具有内部依赖性的请求集时出现锁。直接提交应该要求无界maximumPoolSize以免拒绝任务提交。当命令以超过队列所能处理的平均数到达时,此策略允许无界线程线程具有增长的可能性。

无界队列:使用无界队列(例如不具有预定义容量的LinkedBlockingQueue)将导致所有corePoolSize线程都忙时新任务处在等待状态。这样创建的线程就不会超过corePoolSize,那么maximumPoolSize也就没有意义了。当每个任务之间相互独立时,适合于使用无界队列。当命令以超过队列处理的平均数到达时,此策略允许无界线程具有增长的可能性。

有界队列:当时用有界的maximumPoolSize时,使用有界队列(ArrayBlockingQueue)有助于避免资源的耗尽,但是较难调整和控制,队列大小和最大池大小可能需要相互折中:使用最大队列和小型池可以最大限度的降低CPU使用率,操作系统资源以及上下文切换的开销。但是可能也人工的造成了吞吐量的下降,使用小型池和大队列,则有可能增大了CPU的使用,但是又造成了不可接受的调度开销,同样会降低吞吐量。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java 线程池 线程