ThreadPoolExecutor的一种使用方式

大家都知道通过Executors.newFixedThreadPool(10); 创建出来的线程池有一个阻塞队列,当线程池任务满了以后(达到最大线程数)会将新的任务放到阻塞队列里面,默认情况下该阻塞队列的大小是整数的最大值(其实也就相当于没有限制),当某一个时刻任务量非常大的时候,可能会占满服务器内存,可能会导致服务器完全卡死,从而影响其他服务;另外这两有时也是比较浪费资源的。

在解决办法之前先了解一下线程池中各参数的意思,以及执行过程,源码以及说明如下:

/**
     * Creates a thread pool that reuses a fixed number of threads
     * operating off a shared unbounded queue.  At any point, at most
     * {@code nThreads} threads will be active processing tasks.
     * If additional tasks are submitted when all threads are active,
     * they will wait in the queue until a thread is available.
     * If any thread terminates due to a failure during execution
     * prior to shutdown, a new one will take its place if needed to
     * execute subsequent tasks.  The threads in the pool will exist
     * until it is explicitly {@link ExecutorService#shutdown shutdown}.
     *
     * @param nThreads the number of threads in the pool
     * @return the newly created thread pool
     * @throws IllegalArgumentException if {@code nThreads <= 0}
     */
public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}

/**
     * Creates a new {@code ThreadPoolExecutor} with the given initial
     * parameters and default thread factory.
     *
     * @param corePoolSize the number of threads to keep in the pool, even
     *        if they are idle, unless {@code allowCoreThreadTimeOut} is set
     * @param maximumPoolSize the maximum number of threads to allow in the
     *        pool
     * @param keepAliveTime when the number of threads is greater than
     *        the core, this is the maximum time that excess idle threads
     *        will wait for new tasks before terminating.
     * @param unit the time unit for the {@code keepAliveTime} argument
     * @param workQueue the queue to use for holding tasks before they are
     *        executed.  This queue will hold only the {@code Runnable}
     *        tasks submitted by the {@code execute} method.
     * @param handler the handler to use when execution is blocked
     *        because the thread bounds and queue capacities are reached
     * @throws IllegalArgumentException if one of the following holds:<br>
     *         {@code corePoolSize < 0}<br>
     *         {@code keepAliveTime < 0}<br>
     *         {@code maximumPoolSize <= 0}<br>
     *         {@code maximumPoolSize < corePoolSize}
     * @throws NullPointerException if {@code workQueue}
     *         or {@code handler} is null
     */
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              RejectedExecutionHandler handler) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), handler);
}

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;
}

首先应该明白该方法中各个参数的意思以及线程池执行过程。

一、参数意思其实方法注释就说的很明白:

1.corePoolSize, the number of threads to keep in the pool, even if they are idle, unless {@code allowCoreThreadTimeOut} is set

池中保持活跃的线程数量(核心线程数),就算他们是空闲的,除非给字段allowCoreThreadTimeOut设置了值。

2.maximumPoolSize,the maximum number of threads to allow in the pool

池中允许存在的最大数量的线程数(最大线程数)。

3.keepAliveTime, when the number of threads is greater than the core, this is the maximum time that excess idle threads will wait for new tasks before terminating.

当线程总数比核心线程数大的时候,超出核心线程数的线程在回收之前允许的空闲时间(如果空闲时间超过改时间,则超出核心线程数的线程将会被终止(回收))。

4.unit, the time unit for the {@code keepAliveTime} argument

字段keepAliveTime的时间单位。

5.workQueue, the queue to use for holding tasks before they are executed.  This queue will hold only the {@code Runnable}  tasks submitted by the {@code execute} method.

任务在执行之前存放到该队列,该队列仅存放通过execute方法提交的Runnable任务。

6.handler, the handler to use when execution is blocked because the thread bounds and queue capacities are reached.

当线程池线程数量达到最大并且队列也满了以后需要执行的处理程序(任务拒绝策略)。

7.另外还说明了什么情况下会抛什么异常

注:

参数corePoolSize的注释中提到了除非设置allowCoreThreadTimeOut(可以参考以下源码中的字段注释),该字段是通过方法allowCoreThreadTimeOut设置值的,如果设置成true的话,核心线程空闲的存活时间也是参数传入的keepAliveTime,源码如下:

/**
 * If false (default), core threads stay alive even when idle.
 * If true, core threads use keepAliveTime to time out waiting
 * for work.
 */
private volatile boolean allowCoreThreadTimeOut;

public void allowCoreThreadTimeOut(boolean value) {
        if (value && keepAliveTime <= 0)
            throw new IllegalArgumentException("Core threads must have nonzero keep alive times");
        if (value != allowCoreThreadTimeOut) {
            allowCoreThreadTimeOut = value;
            if (value)
                interruptIdleWorkers();
        }
}

二、执行过程

1.当线程数小于corePoolSize时,新提交任务就会创建一个线程来处理,就算有空闲线程;

2.当线程数达到corePoolSize,并且没有空闲线程的时候,会将任务放到workQueue队列中等待处理;

3.当workQueue队列已经满了,并且maximumPoolSize大于corePoolSize时,创建线程来执行任务;

4.当线程数达到maximumPoolSize时,提交的任务会交给RejectedExecutionHandler(任务拒绝策略);

另外当大于corePoolSize的那些线程处于空闲并且空闲时间达到keepAliveTime的时候,线程会被回收(终止),如果将allowCoreThreadTimeOut设置成true,则核心线程也在空闲时间达到keepAliveTime的时候被回收。

以下是对该线程池的一个运用实例:

首先自己新建一个类,并继承ThreadPoolExecutor类,因为newFixedThreadPool方法创建的其实也是该类的对象,newFixedThreadPool方法的源码如下(包括方法注释):

自己创建的类的代码如下:

package mythreadpool;
import java.util.concurrent.*;

public class MyThreadPoolExecutor extends ThreadPoolExecutor {

    public MyThreadPoolExecutor() {
        super(10, //核心线程数(该参数可以根据实际系统资源进行设置,可以参考该值:Runtime.getRuntime().availableProcessors())
                10,   //最大线程数
                0,  //空闲线程存活时间
                TimeUnit.MILLISECONDS,  //时间单位
                new LinkedBlockingDeque<>(1000),  //阻塞队列的大小可以根据实际情况设置
                new MyRejectedExecutionHandler());  //此处指定使用的任务拒绝策略
    }
}

既然处理的是阻塞队列满了的情况,就必须要考虑当队列满了以后任务的拒绝策略,所以还应该实现自己的任务拒绝策略,如下:

package mythreadpool;

import org.springframework.stereotype.Service;

@Service
public class MyRejectedExecutionHandler implements RejectedExecutionHandler {

    @Override
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {

        MyWorker task = (MyWorker) r;
        Object obj = task.getObj();  //获取到任务

        //在此可以实现自己的任务拒绝策略
    }
}

到此,基于ThreadPoolExecutor的自己的线程池以及任务拒绝策略已经完成,使用方法如下:

package mythreadpool;

public class MyWorker implements Runnable {

    private Object obj;

    public MyWorker (Object obj) {
        this.obj = obj;
    }

    @Override
    public void run() {
        try {
            //实现任务
        } catch (Exception e) {
            
        }
    }

    public Object getObj() {
        return obj;
    }
}

调用方式如下:

private static final ExecutorService myService = new MyThreadPoolExecutor();

myService.execute(new MyWorker(new Object()));

在此推荐两篇感觉不错的博文,有兴趣的同学可以阅读一下:

https://blog.csdn.net/qq_29428215/article/details/85784071

https://www.cnblogs.com/zedosu/p/6665306.html

猜你喜欢

转载自blog.csdn.net/u012131610/article/details/87289985