java线程池 ThreadPoolExecutor

阿里巴巴的java开发规范上说线程池要自己用new创建,方便开发理解线程池的各个参数。


    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.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

参数介绍:

corePoolSize 核心线程数,默认情况下核心线程会一直存活,即使处于闲置状态也不会受存keepAliveTime限制。除非将allowCoreThreadTimeOut设置为true。

maximumPoolSize 指的是线程池的最大大小(线程池中最大有corePoolSize 个线程可运行)。 

keepAliveTime 指的是空闲线程结束的超时时间(当一个线程不工作时,过keepAliveTime 长时间将停止该线程)。 

unit 是一个枚举,表示 keepAliveTime 的单位(有NANOSECONDS, MICROSECONDS, MILLISECONDS, SECONDS, MINUTES, HOURS, DAYS,7个可选值)。 

workQueue 表示存放任务的队列(存放需要被线程池执行的线程队列)。

threadFactory 线程工厂,提供创建新线程的功能。ThreadFactory是一个接口,只有一个方法

public interface ThreadFactory {

    Thread newThread(Runnable r);
}

通过线程工厂可以对线程的一些属性进行定制,比如线程名。

handler 拒绝策略(添加任务失败后如何处理该任务).RejectedExecutionHandler是一个接口,只有一个方法

public interface RejectedExecutionHandler {

    void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}

 在这个方法中可以自己进行处理,比如日志记录被拒绝的任务。

运行原理

1、线程池刚创建时,里面没有一个线程。

2、当调用 execute() 方法添加一个任务时,线程池会做如下判断:

    a. 如果正在运行的线程数量小于 corePoolSize,那么马上创建线程运行这个任务;

    b. 如果正在运行的线程数量大于或等于 corePoolSize,那么将这个任务放入队列。

    c. 如果这时候队列满了,而且正在运行的线程数量小于 maximumPoolSize,那么还是要创建线程运行这个任务;

    d. 如果队列满了,而且正在运行的线程数量大于或等于 maximumPoolSize,那么线程池会调用RejectedExecutionHandler的          rejectedExecution方法抛出异常。

3、当一个线程完成任务时,它会从队列中取下一个任务来执行。

4、当一个线程无事可做,超过一定的时间(keepAliveTime)时,线程池会判断,如果当前运行 的线程数大于 corePoolSize,那么这个线程就被停掉。所以线程池的所有任务完成后,它最终会收缩到 corePoolSize 的大小。

任务队列

SynchronousQueue

    /**
     * Always returns zero.
     * A {@code SynchronousQueue} has no internal capacity.
     *
     * @return zero
     */
    public int size() {
        return 0;
    }

    /**
     * Always returns zero.
     * A {@code SynchronousQueue} has no internal capacity.
     *
     * @return zero
     */
    public int remainingCapacity() {
        return 0;
    }

这个队列的size和remainingCapacity(可用数量)永远返回0,线程池认为等待队列已经满了。

就是说用这个队列,等待队列的size为0,有新的任务加入,会直接执行(用空闲线程或创建新的线程),如果线程数量达到max,那么线程池会抛出异常。

ArrayBlockingQueue 和 LinkedBlockingQueue

一个是数组实现,一个是链表实现,可以指定队列长度。

ArrayBlockingQueue和LinkedBlockingQueue是两个最普通、最常用的阻塞队列。

拒绝策略RejectedExecutionHandler

ThreadPoolExecutor中提供了几种策略可以使用:

  • AbortPolicy
  • DiscardPolicy
  • DiscardOldestPolicy
  • CallerRunsPolicy

AbortPolicy 

该策略是线程池的默认策略。使用该策略时,如果线程池队列满了丢掉这个任务并且抛出RejectedExecutionException异常。

public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    throw new RejectedExecutionException("Task " + r.toString() +
                                                 " rejected from " +
                                                 e.toString());
}

DiscardPolicy

如果线程池队列满了,会直接丢掉这个任务并且不会有任何异常。

public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}

DiscardOldestPolicy

将最早进入队列的任务删掉,再尝试加入队列。

public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    if (!e.isShutdown()) {
        e.getQueue().poll();
        e.execute(r);
    }
}

CallerRunsPolicy

使用此策略,如果添加到线程池失败,那么主线程(执行execute()方法的线程)会自己去执行该任务,不会等待线程池中的线程去执行。

public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    if (!e.isShutdown()) {
        r.run();
    }
}

 自定义

如果以上策略都不符合需求,可以自己定义拒绝策略,实现RejectedExecutionHandler接口,并实现rejectedExecution方法就可以了。具体的逻辑就在rejectedExecution方法中定义。

猜你喜欢

转载自blog.csdn.net/qq_27273637/article/details/85231437