线程池由浅入深-手动创建线程池

 JDK提供了一个ThreadPoolExecutor类供我们来手动创建线程池,类定义如下:

public class ThreadPoolExecutor extends AbstractExecutorService

1. 构造方法

 想要通过ThreadPoolExecutor类来创建线程池,实质上就是向其构造方法传入不同参数来获取不同的线程池。

 看具体的构造方法之前,我们先看一下其中会涉及到的参数:

参数名 含义
corePoolSize 线程池维护线程的最少数量。线程池至少会保持改数量的线程存在,即使没有任务可以处理。(注意:这里说的至少是指线程达到这个数量后,即使有空闲的线程也不会释放,而不是说线程池创建好之后就会初始化这么多线程)
maximumPoolSize 线程池最大数量,线程池允许创建的最大线程数,如果队列满了,并且已创建的线程数小于最大线程数,则线程池会再创建新的线程来执行任务。值得注意的是,线程池队列如果使用的是无界队列,那么这个参数就没有什么效果
uint 线程活动保持时间的单位,可选择的时间有时分秒等,即线程池维护线程所允许的空闲时间的单位,和keepAliveTime配合使用
keepAliveTime 线程活动保持时间,线程池的工作线程空闲后,保持存活的时间,所以,当任务很多的时候,并且每个任务执行的时间比较短,可以调大时间,即调大线程活动保持时间,可以提高线程的利用率
workQueue 任务队列,用于暂时保存任务的工作队列
threadFactory 用于创建线程的工厂
handler 饱和策略

 构造方法如下:

  • 使用给定的参数,工作队列,饱和策略以及工厂方法来创建线程池,这是ThreadPoolExecutor最核心的构造方法,其它的构造是在其基础上的重载。
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;
    }
  • 使用给定的参数,工作队列,默认的饱和策略和工厂方法来创建线程池。
public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }
  • 使用给定的参数,工作队列,工厂方法,默认的饱和策略来创建线程池。
public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             threadFactory, defaultHandler);
    }
  • 使用给定的参数,工作队列,饱和策略,默认的工厂方法来创建线程池。
public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              RejectedExecutionHandler handler) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), handler);
    }

2. 工作队列

 在线程池中,一般使用一个队列进行任务和线程池中线程进行耦合,工作队列有如下几种:

  • ArrayBlockingQueue:

 基于数组结构的有界阻塞队列,此队列按照FIFO(先进先出)原则对元素进行排序。

  • LinkedBlockingQueue:

 基于链表结构的有界阻塞队列,也按照FIFO排序元素,吞吐量高于ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool(n)使用了此队列。

  • PriorityBlockingQueue:

 具有优先级的无限阻塞队列,即优先队列。

  • SynchronousQueue:

 一个不存储元素的阻塞队列。每个插入操作必须等待另一个线程调用移除操作,否则插入操作一直处于阻塞状态,可以视为只有一个元素的队列。吞吐量要高于LinkedBlockingQueue,静态工厂方法Executors.newCachedThreadPool()使用了此队列。

3. 饱和策略

 饱和策略又称拒绝策略,指的是当线程池中每个线程都在承载线程任务时,这时如果又有了新的线程任务,线程池将会采用的策略,常见线程策略如下:

  • AbortPolicy:

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

  • CallerRunsPolicy:只用调用这所在的线程来运行任务

 线程池队列满了,会直接丢弃新加入的任务并且不会抛出异常。

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

 如果队列满了,会将最早进入队列的线程任务退出,再尝试将新的任务加入队列。

  • DiscardPolicy:

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

  • 自定义

 JDK允许我们自定义饱和策略,只要实现RejectedExecutionHandler接口,并且实现rejectedExecution方法就可以了,rejectedExecution方法中定义饱和策略的逻辑代码。

4. 手动创建线程池

 接下来,使用 ThreadPoolExecutor类进行线程池的创建。

public class MyThreadPool {

    /**
     * 基本参数
     */
    static int corePoolSize = 10;

    static int maximumPoolSizeSize = 100;

    static long keepAliveTime = 1;

    static ArrayBlockingQueue workQueue = new ArrayBlockingQueue(10);


    public static void main(String[] args) {

        // 使用默认饱和策略创建线程池
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                corePoolSize,
                maximumPoolSizeSize,
                keepAliveTime,
                TimeUnit.SECONDS,
                workQueue,
                // JDK1.8后此方法作废
                new ThreadFactoryBuilder().setNameFormat("XX-task-%d").build());
        // 向线程池中添加任务
        executor.execute(() -> System.out.println("ok"));
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_40739833/article/details/81176079