ThreadPoolExecutor使用

来源:互联网 发布:矩阵归一化处理 编辑:程序博客网 时间:2024/05/17 12:04

ThreadPoolExecutor是 java提供的一个线程池工具类

 public ThreadPoolExecutor(int corePoolSize,                              int maximumPoolSize,                              long keepAliveTime,                              TimeUnit unit,                              BlockingQueue<Runnable> workQueue,                              ThreadFactory threadFactory,                              RejectedExecutionHandler handler) {        if (corePoolSize < 0 ||            maximumPoolSize <= 0 ||            maximumPoolSize < corePoolSize ||            keepAliveTime < 0)            throw new IllegalArgumentException();        if (workQueue == null || threadFactory == null || handler == null)            throw new NullPointerException();        this.corePoolSize = corePoolSize;        this.maximumPoolSize = maximumPoolSize;        this.workQueue = workQueue;        this.keepAliveTime = unit.toNanos(keepAliveTime);        this.threadFactory = threadFactory;        this.handler = handler; }


corePoolSize 最小线程数
maximumPoolSize 最大线程数

keepAliveTime  线程最大空闲时间 (当线程数大于 corePoolSize时,剩余的线程的空闲时间大于keepAliveTime将会被回收

unit   keepAliveTime对应的时间单位

workQueue 任务队列

threadFactory 线程工厂

handler  (饱和策略) 当线程数达到最大,且任务队列已满,就会调用 此handler的方法处理


默认情况下 不传入 threadFactory会提供一个默认的值,Executors.defaultThreadFactory()

不过默认的线程池名字是很揪心的,格式都是 pool-poolNumber-thread-threadNumber

所以要是创建了好几个线程池,名字基本就是pool-1-thread-1,pool-1-thread-2,  pool-2-thread-1, pool-2-thread-2,完全没有可读性

 DefaultThreadFactory() {            SecurityManager s = System.getSecurityManager();            group = (s != null) ? s.getThreadGroup() :                                  Thread.currentThread().getThreadGroup();            namePrefix = "pool-" +                          poolNumber.getAndIncrement() +                         "-thread-";        }
google 的guava提供了一个方法设置线程池名称 new ThreadFactoryBuilder().setNameFormat("scheduler-pool-%d").build();  

这样一看就知道是什么线程了

如果不想引入google的guava,就自己写个类继承ThreadFactory,

public class MqThreadFactory implements ThreadFactory {    private String poolName;    private final ThreadGroup group;    private final AtomicInteger threadNumber = new AtomicInteger(1);    private final String namePrefix;    public MqAppThreadFactory(String poolName) {        SecurityManager s = System.getSecurityManager();        group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();        namePrefix = poolName + "-pool" + "-thread-";    }    public Thread newThread(Runnable r) {        Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0);        if (t.isDaemon())            t.setDaemon(false);        if (t.getPriority() != Thread.NORM_PRIORITY)            t.setPriority(Thread.NORM_PRIORITY);        return t;    }}
调用的时候传入poolName就行了

java提供了几种饱和策略供选择

AbortPolicy  当线程数达到最大,且任务队列已满,直接抛出异常,这也是ThreadPoolExecutor的默认饱和策略

DiscardPolicy 直接丢弃任务

DiscardOldestPolicy 丢弃队列里最近的一个任务,并执行当前任务

CallerRunsPolicy  只用用者所在线程来运行任 

threadPoolExecutor使用方式:

ExecutorService pool=new ThreadPoolExecutor(coreThreadSize, maxThreadSize,keepAliveTime, unit,                queue, new MqThreadFactory(poolName)
这里使用默认的饱和策略


然后提交任务到线程池就行了
executorService.execute(new QueueHandler());

当然也可以调用 executorservice.submit 方法

提交到线程池的任务并不一定会马上执行,之前碰到一个问题,线程的代码默认监听BlockingQueue,

希望提高性能,所以项目初始化的时候,循环调用execute方法,希望线程池中的所有线程默认都开启,

结果发现,线程数维持在,corePoolSize,

下面是execute方法的执行流程:

1、线程池判断核心线程池里的线程是否都在执行任务。如果不是,则创建一个新的工作
线程来执行任务。如果核心线程池里的线程都在执行任务,则进入下个流程。
2、线程池判断工作队列是否已经满。如果工作队列没有满,则将新提交的任务存储在这
个工作队列里。如果工作队列满了,则进入下个流程。
3、线程池判断线程池的线程是否都处于工作状态。如果没有,则创建一个新的工作线程
来执行任务。如果已经满了,则交给饱和策略来处理这个任务

具体的大家可以看看 java并发编程的艺术 这本书,个人觉得在某些细节上面讲的比  java并发编程实战要好