码迷,mamicode.com
首页 > 其他好文 > 详细

ThreadPoolExecutor知识点详解

时间:2015-12-10 18:54:47      阅读:1119      评论:0      收藏:0      [点我收藏+]

标签:

先贴基本实现代码:

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor.AbortPolicy;
import java.util.concurrent.TimeUnit;

public class ThreadPoolExecutorTest {

/**
* @param args
*/
public static void main(String[] args) {

ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 5, 100,TimeUnit.MINUTES, new LinkedBlockingQueue(10), new AbortPolicy());
for(int i=0;i<15;i++){
MyTask myTask = new MyTask(i);
executor.execute(myTask);
System.out.println("线程池中线程数目:"+executor.getPoolSize()+",队列中等待执行的任务数目:"+
executor.getQueue().size()+",已执行玩别的任务数目:"+executor.getCompletedTaskCount());
}
executor.shutdown();
}
}

class MyTask implements Runnable {
private int taskNum;

public MyTask(int num) {
this.taskNum = num;
}

@Override
public void run() {
System.out.println("正在执行task " + taskNum);
try {
Thread.currentThread().sleep(40);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("task " + taskNum + "执行完毕");
}
}

ThreadPoolExecutor 是线程池执行类, 基于ThreadPoolExecutor可以很容易将一个Runnable接口的任务放入线程池中

学习ThreadPoolExceutor主要学习三大扩展类和四构造函数:

1、构造函数:

技术分享

这是ThreadPoolExceutor的构造函数 前三个是基于第四个实现的(可以查看相关源代码)现在来剖析构造器参数意义:

 参数解释
corePoolSize:         核心线程数,会一直存活,即使没有任务,线程池也会维护线程的最少数量
maximumPoolSize:  线程池维护线程的最大数量
keepAliveTime:        线程池维护线程所允许的空闲时间,当线程空闲时间达到keepAliveTime,该线程会退出,直到线程数量等于corePoolSize。如果allowCoreThreadTimeout设置为true,则所有线程均会退出直到线程数量为0。
unit: 线程池维护线程所允许的空闲时间的单位、可选参数值为:TimeUnit中的几个静态属性:NANOSECONDS、MICROSECONDS、MILLISECONDS、SECONDS。
workQueue: 线程池所使用的缓冲队列,常用的是:java.util.concurrent.ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue  其中的区别可以看博主前一篇文章  

ArrayBlockingQueue有边界 但是如果线程数>maximumPoolSize+workQueue.size()会交给handler处理      

LinkedBlockingQueue初始化可以是无限大 但是也可以给定限制大小 

SynchronousQueue  是一个特殊的LinkedBlockingQueue 相当于new LinkedBlockingQueue(1)

 

排队有三种通用策略:

 

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

 

无界队列。使用无界队列(例如,不具有预定义容量的 LinkedBlockingQueue)将导致在所有 corePoolSize 线程都忙时新任务在队列中等待。这样,创建的线程就不会超过 corePoolSize。(因此,maximumPoolSize的值也就无效了。)当每个任务完全独立于其他任务,即任务执行互不影响时,适合于使用无界队列;例如,在 Web页服务器中。这种排队可用于处理瞬态突发请求,当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。

 

有界队列。当使用有限的 maximumPoolSizes时,有界队列(如 ArrayBlockingQueue)有助于防止资源耗尽,但是可能较难调整和控制。队列大小和最大池大小可能需要相互折衷:使用大型队列和小型池可以最大限度地降低 CPU 使用率、操作系统资源和上下文切换开销,但是可能导致人工降低吞吐量。如果任务频繁阻塞(例如,如果它们是 I/O边界),则系统可能为超过您许可的更多线程安排时间。使用小型队列通常要求较大的池大小,CPU使用率较高,但是可能遇到不可接受的调度开销,这样也会降低吞吐量。 

 

 

handler: 线程池中的数量大于maximumPoolSize,对拒绝任务的处理策略,默认值ThreadPoolExecutor.AbortPolicy()  相关策略还有

1、AbortPolicy:直接抛出异常。2、CallerRunsPolicy:只用调用者所在线程来运行任务。3、DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务。4、DiscardPolicy:不处理,丢弃掉。

5、当然也可以根据应用场景需要来实现RejectedExecutionHandler接口自定义策略。如记录日志或持久化不能处理的任务。 

线程池按以下行为执行任务 1、 当线程数小于核心线程数时,创建线程。2、当线程数大于等于核心线程数,且任务队列未满时,将任务放入任务队列。3、当线程数大于等于核心线程数,且任务队列已满 若线程数小于最大线程数,创建线程 若线程数等于最大线程数,抛出异常,拒绝任务

2、扩展类:(回家在写)

                                                                                                        线程池的比较重要的几个类:

ExecutorService

真正的线程池接口。

ScheduledExecutorService

能和Timer/TimerTask类似,解决那些需要任务重复执行的问题。

ThreadPoolExecutor

ExecutorService的默认实现。

ScheduledThreadPoolExecutor

继承ThreadPoolExecutor的ScheduledExecutorService接口实现,周期性任务调度的类实现。

 

一、固定大小的线程池 

 ExecutorService pool = Executors.newFixedThreadPool(2);

pool.execute(t1);  

实际是一个:

 

new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>())

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

二、单任务线程池 

 ExecutorService pool = Executors.newSingleThreadExecutor();   

实际是一个:

new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>())

创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。 

对于以上两种连接池,大小都是固定的,当要加入的池的线程(或者任务)超过池最大尺寸时候,则入此线程池需要排队等待。 

 

三、可变尺寸的线程池 

与上面的类似,只是改动下pool的创建方式: 

ExecutorService pool = Executors.newCachedThreadPool();  

实际是一个:

new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());

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

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

这个实现就有意思了。首先是无界的线程池,所以我们可以发现maximumPoolSize为big big。其次BlockingQueue的选择上使用SynchronousQueue。可能对于该BlockingQueue有些陌生,简单说:该QUEUE中,每个插入操作必须等待另一个线程的对应移除操作。

 

四、延迟连接池

ScheduledExecutorService pool = Executors.newScheduledThreadPool(2);  

scheduler.scheduleAtFixedRate(new HourTimer(), 0, 1, TimeUnit.HOURS); //在 1 小时中继续执行

scheduler.scheduleWithFixedRate(new HourTimer(), 0, 1, TimeUnit.HOURS);

实际是一个:

super(corePoolSize, Integer.MAX_VALUE, 0, TimeUnit.NANOSECONDS,new DelayedWorkQueue());

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

技术分享

ThreadPoolExecutor知识点详解

标签:

原文地址:http://www.cnblogs.com/djie/p/5026759.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!