线程池的创建方式和执行过程的理解

准备面试题时看到多线程中的线程池,记录一下内容,加深下记忆

  1. 线程池的创建

线程池的创建方法总共有 7 种,但总体来说可分为 2 类:

  • 一类是通过 ThreadPoolExecutor 创建的线程池;

  • 另一个类是通过 Executors 创建的线程池。

1.1、Executors创建线程池

线程池工厂(Executors)使用静态工程方法为我们封装好了 6 种常见的功能线程池

1.1.1、定长,线程池

Executors.newFixedThreadPool:创建一个固定大小的线程池,可控制并发的线程数,超出的线程会在队列中等待;

  • 特点:只有核心线程,线程数量固定,执行完立即回收,任务队列为链表结构的有界队列(任务队列的类型和解释会在下一类线程池创建方式中提到)。

  • 应用场景:控制线程最大并发数。

  • 静态方法

/**创建一个线程池,重用固定数量的线程  
 *操作一个共享的无界队列。 在任何时候,最多  
 *只能有{@code nThreads}数量的线程活动的处理任务。  
 *如果所有线程都处于活动状态时提交了额外的任务,  
 *它们将在队列中等待,直到有线程可用。  
 *如果任何线程在执行过程中由于失败而终止  
 *在关闭前,如有需要,将会有一个新的取代它的位置  
 *执行后续任务。 池中的线程将一直存在  
 *直到显式地被{@link ExecutorService#shutdown shutdown}。    
@param nThreads线程池中的线程数  
* @返回新建的线程池  
* @抛出IllegalArgumentException如果{@code nThreads <= 0}  
*/ 
public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

/**
 * @param nThreads the number of threads in the pool
 * @param threadFactory the factory to use when creating new threads
 * @return the newly created thread pool
 */
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>(),
                                      threadFactory);
    }
  • 使用示例如下:

public static void fixedThreadPool() {
    // 创建 2 个线程数量的线程池
    ExecutorService threadPool = Executors.newFixedThreadPool(2);

    // 创建线程任务
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            System.out.println("任务被执行,线程:" + Thread.currentThread().getName());
        }
    };

    // 线程池执行任务(一次添加 4 个任务)
    // 执行任务的方法有两种:submit 和 execute
    threadPool.submit(runnable);  // 执行方式 1:submit
    threadPool.execute(runnable); // 执行方式 2:execute
    threadPool.execute(runnable);
    threadPool.execute(runnable);
}
1.1.2、可缓存线程池

Executors.newCachedThreadPool:创建一个可缓存的线程池,若线程数超过处理所需,缓存一段时间后会回收,若线程数不够,则新建线程;

  • 特点:无核心线程,非核心线程数量无限,执行完闲置 60s 后回收,任务队列为不存储元素的阻塞队列。

  • 应用场景:执行大量、耗时少的任务。

  • 静态方法

/**
*创建一个线程池,根据需要创建新线程,如果线程池中有之前创建好的线程且该线程处于空闲状态将重用之前构造的线程  
*这个方法创建的线程池适合执行许多短期异步任务的线程
*调用线程池去执行任务时,如果之前创建好的线程可用将重用先前构造的代码。 如果没有现有线程可用,则创建一个新的线程  
*线程将被创建并添加到池中,当创建的线程空闲时间超过60秒,这个线程将被停止并从缓存池中移除。 
*因此,一个保持空闲足够长的池将会不消耗任何资源。 请注意具有相似的池 
*/
public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }


public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>(),
                                      threadFactory);
    }
  • 使用示例如下:

public static void cachedThreadPool() {
    // 创建线程池
    ExecutorService threadPool = Executors.newCachedThreadPool();
    // 执行任务
    for (int i = 0; i < 10; i++) {
        threadPool.execute(() -> {
            System.out.println("任务被执行,线程:" + Thread.currentThread().getName());
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
            }
        });
    }
}
//执行结果显示会有十个线程被创建出来
1.1.3、单个线程化线程池

Executors.newSingleThreadExecutor:创建单个线程数的线程池,它可以保证先进先出的执行顺序;

  • 特点:只有 1 个核心线程,无非核心线程,执行完立即回收,任务队列为链表结构的有界队列。

  • 应用场景:不适合并发的可能引起 IO 阻塞性及影响 UI 线程响应的操作,如数据库操作、文件操作等。

  • 静态方法

/ * *  
*创建一个Executor,使用一个工作线程操作  
*关闭一个无界队列
*(请注意,如果这个单线程在执行之前由于故障而终止,将关闭这个线程,之后创建一个新的线程取代它的地方,去执行后续任务。)从而保证完成提交的任务。  
*并且在任何时刻都不会有一个以上的任务处于活动状态,给定时间,否则就是个无效项   
*{@code newFixedThreadPool(1)} 保证不能重新配置使用额外的线程。  
* @返回新创建的单线程Executor  
* / 
public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }


public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>(),
                                    threadFactory));
    }
  • 使用示例如下:

public static void singleThreadExecutor() {
    // 创建线程池
    ExecutorService threadPool = Executors.newSingleThreadExecutor();
    // 执行任务
    for (int i = 0; i < 10; i++) {
        final int index = i;
        threadPool.execute(() -> {
            System.out.println(index + ":任务被执行");
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
            }
        });
    }
}

//执行结果显示index是顺序打印的,这就证明单个线程数的线程池保证了所有任务按照指定顺序在一个线程中执行
1.1.4、定时线程池

Executors.newScheduledThreadPool:创建一个可以执行延迟任务的线程池;

  • 特点:核心线程数量固定,非核心线程数量无限,执行完闲置 10ms 后回收,任务队列为延时阻塞队列。

  • 应用场景:执行定时或周期性的任务。

  • 静态方法

/**
*创建一个线程池,可以调度命令在给定的时间后延迟执行,或定期执行。
*设置线程池中的线程数,即使他们是空闲的
* @返回新创建的调度线程池
如果{@code corePoolSize < 0}将抛出IllegalArgumentException
* /
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }

public static ScheduledExecutorService newScheduledThreadPool(
            int corePoolSize, ThreadFactory threadFactory) {
        return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
    }
  • 使用示例如下:


public static void scheduledThreadPool() {
    // 创建线程池
    ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(5);
    // 添加定时执行任务(1s 后执行)
    System.out.println("添加任务,时间:" + new Date());
    threadPool.schedule(() -> {
        System.out.println("任务被执行,时间:" + new Date());
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
        }
    }, 1, TimeUnit.SECONDS);
}
//执行结果显示任务在 1 秒之后被执行了,符合我们的预期。
1.1.5、单线程定时线程池
  1. Executors.newSingleThreadScheduledExecutor:创建一个单线程的可以执行延迟任务的线程池;

 static class DelegatedExecutorService extends AbstractExecutorService {
        private final ExecutorService e;
        DelegatedExecutorService(ExecutorService executor) { e = executor; }
        public void execute(Runnable command) { e.execute(command); }
        public void shutdown() { e.shutdown(); }
        public List<Runnable> shutdownNow() { return e.shutdownNow(); }
        public boolean isShutdown() { return e.isShutdown(); }
        public boolean isTerminated() { return e.isTerminated(); }
        public boolean awaitTermination(long timeout, TimeUnit unit)
            throws InterruptedException {
            return e.awaitTermination(timeout, unit);
        }

 static class DelegatedScheduledExecutorService
            extends DelegatedExecutorService
            implements ScheduledExecutorService {
        private final ScheduledExecutorService e;
        DelegatedScheduledExecutorService(ScheduledExecutorService executor) {
            super(executor);
            e = executor;
        }
/**
  *创建一个单线程的可以执行延迟任务的线程池。
  */
public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
        return new DelegatedScheduledExecutorService
            (new ScheduledThreadPoolExecutor(1));
    }

public static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory) {
        return new DelegatedScheduledExecutorService
            (new ScheduledThreadPoolExecutor(1, threadFactory));
    }
1.1.6、任务窃取线程池

Executors.newWorkStealingPool:创建一个抢占式执行的线程池(任务执行顺序不确定),不同于上面的线程池是基于ThreadPoolExecutor 创建,这个方法是基于ForkJoinPool 的扩展.算法思想就是窃取算法,ForkJoinPool的优势在于,可以充分利用多cpu,多核cpu的优势,把一个任务拆分成多个“小任务”分发到不同的cpu核心上执行,执行完后再把结果收集到一起返回。

之前的线程池中,多个线程共有一个阻塞队列,而newWorkStealingPool 中每一个线程都有一个自己的队列。

当线程发现自己的队列没有任务了,就会到别的线程的队列里获取任务执行。可以简单理解为”窃取“。

一般是自己的本地队列采取LIFO(后进先出),窃取时采用FIFO(先进先出),一个从头开始执行,一个从尾部开始执行,由于偷取的动作十分快速,会大量降低这种冲突,也是一种优化方式。

  • 特点:线程的数量,如果传入参数,线程数量就是参数的数值,如果不传入,则默认使用当前计算机中可用的cpu数量

能够合理的使用CPU进行对任务操作(并行操作)

  • 应用场景:适合处理很耗时的线程。

  • 静态方法

 /**
 *根据参数parallelism的数值创建一个线程池来维护足够的线程,并可以使用多个队列来减少竞争。 
 *@param parallelism并行度对应于最大线程数
 *进行任务处理的实际的线程数可能动态增长和收缩。 
 *该线程池不会保证提交任务的执行顺序。
 * @param parallelism目标并行度
 * @返回新建的线程池
 如果{@代码并行度<= 0}
 * @自1.8
 * /
public static ExecutorService newWorkStealingPool(int parallelism) {
        return new ForkJoinPool
            (parallelism,
             ForkJoinPool.defaultForkJoinWorkerThreadFactory,
             null, true);
    }

/**
*使用可用处理器作为目标并行度创建工作窃取线程池
* {@link Runtime#availableProcessors运行程序主机的可用处理器。
* @返回新建的线程池
* @see #newWorkStealingPool(int)
* @自1.8
* /
  public static ExecutorService newWorkStealingPool() {
        return new ForkJoinPool
            (Runtime.getRuntime().availableProcessors(),
             ForkJoinPool.defaultForkJoinWorkerThreadFactory,
             null, true);
    }

/**
*创建一个带有给定参数的{@code ForkJoinPool}。
*
* @param parallelism并行度。对于默认值,
*使用{@link java.lang.Runtime#availableProcessors}运行程序主机的可用处理器。
@param factory创建新线程的工厂。对于默认值,
*使用{@link #defaultForkJoinWorkerThreadFactory}。
* @param handler内部工作线程的处理程序
*任务执行时遇到不可恢复的错误而终止线程因未知异常而终止的回调处理。
对于默认值,使用{@code null}。
* @param asyncMode是否异步
如果为true,
*分解未连接的任务时,建立本地先进先出的调度模式
*。这种模式可能更合适
*基于本地堆栈,应用程序中默认模式是工作线程只处理事件类型的异步任务。
*默认值使用{@code false}。
如果并行度小于或,抛出IllegalArgumentException
*等于零,或大于实现限制
如果工厂为空,则抛出NullPointerException
如果存在安全管理器,则抛出SecurityException
调用者不允许修改线程
*因为它不包含{@link
*         . lang。RuntimePermission} {@code (modifyThread)}
* /
public ForkJoinPool(int parallelism,
                        ForkJoinWorkerThreadFactory factory,
                        UncaughtExceptionHandler handler,
                        boolean asyncMode) {
        this(checkParallelism(parallelism),
             checkFactory(factory),
             handler,
             asyncMode ? FIFO_QUEUE : LIFO_QUEUE,
             "ForkJoinPool-" + nextPoolId() + "-worker-");
        checkPermission();
    }
  • 使用示例如下:


public static void workStealingPool() {
    // 创建线程池
    ExecutorService threadPool = Executors.newWorkStealingPool();
    // 执行任务
    for (int i = 0; i < 10; i++) {
        final int index = i;
        threadPool.execute(() -> {
            System.out.println(index + " 被执行,线程名:" + Thread.currentThread().getName());
        });
    }
    // 确保任务执行完成
    while (!threadPool.isTerminated()) {
    }
}

//执行结果显示任务的执行顺序是不确定的,因为它是抢占式执行的。

1.2、ThreadPoolExecutor创建线程池

  • 特点:可以根据我们的意愿来创建线程池,核心线程数、最大线程数、排队队列、拒绝策略等都可以自己控制,更加灵活。

  • 应用场景: Executors 自动创建线程的方式,因为线程个数或者任务个数不可控,可能会导致内存溢出的风险,所以在创建线程池时,建议使用 ThreadPoolExecutor 的方式来创建。

  • 静态方法及所需参数的解释

  /**
     * Creates a new {@code ThreadPoolExecutor} with the given initial
     使用给定的初始值创建一个新的{@code ThreadPoolExecutor}线程池
     * parameters.
     *
     * @param corePoolSize the number of threads to keep in the pool, even
     *        if they are idle, unless {@code allowCoreThreadTimeOut} is set
     核心线程数。默认情况下,核心线程会一直存活,但是当将 allowCoreThreadTimeout 设置为 true 时,核心线程      也会超时回收。
     * @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.
     线程闲置超时时长。如果超过该时长,非核心线程就会被回收。如果将 allowCoreThreadTimeout 设置为 true        时,核心线程也会超时回收。
     * @param unit the time unit for the {@code keepAliveTime} argument
     指定 keepAliveTime 参数的时间单位。常用的有:TimeUnit.MILLISECONDS(毫秒)、TimeUnit.SECONDS      (秒)、TimeUnit.MINUTES(分)。
     * @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.
     任务队列。通过线程池的 execute() 方法提交的 Runnable 对象将存储在该参数中。其采用阻塞队列实现。
     * @param threadFactory the factory to use when the executor
     *        creates a new thread
     线程工厂。用于指定为线程池创建新线程的方式。
     * @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 threadFactory} or {@code handler} is null
     */
 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;
    }
  • 示列如下

ThreadPoolExecutor threadPool  = new ThreadPoolExecutor(

                                                    int corePoolSize,

                                                    int maximumPoolSize,

                                                    long keepAliveTime,

                                                    TimeUnit unit,

                                                    BlockingQueue<Runnable> workQueue,

                                                    ThreadFactory threadFactory,

                                                    RejectedExecutionHandler handler

                                    )
//向线程池提交交任务
threadPool.execute(new Runnable() {
    @Override
    public void run() {
        ... // 线程执行的任务
    }
});
// 关闭线程池
threadPool.shutdown(); // 设置线程池的状态为SHUTDOWN,然后中断所有没有正在执行任务的线程
threadPool.shutdownNow(); // 设置线程池的状态为 STOP,然后尝试停止所有的正在执行或暂停任务的线程,并返回等待执行任务的列表
1.2.1、对于参数任务队列、线程工厂和拒绝策略的解释补充
  • 阻塞队列工作原理

阻塞队列(BlockingQueue)是一个支持两个附加操作的队列。这两个附加的操作支持阻塞的插入和移除方法

1)支持阻塞的插入方法:意思是当队列满时,队列会阻塞插入元素的线程,直到队列不满。

2)支持阻塞的移除方法:意思是在队列为空时,获取元素的线程会等待队列变为非空

阻塞队列常用于生产者和消费者的场景,生产者是向队列里添加元素的线程,消费者是从队列里取元素的线程。阻塞队列就是生产者用来存放元素、消费者用来获取元素的容器。

在阻塞队列不可用时,这两个附加操作提供了4种处理方式

抛出异常:是指当阻塞队列满时候,再往队列里插入元素,会抛出 IllegalStateException("Queue full") 异常。当队列为空时,从队列里获取元素时会抛出 NoSuchElementException 异常 。

返回特殊值:插入方法会返回是否成功,成功则返回 true。移除方法,则是从队列里拿出一个元素,如果没有则返回 null

一直阻塞:当阻塞队列满时,如果生产者线程往队列里 put 元素,队列会一直阻塞生产者线程,直到拿到数据,或者响应中断退出。当队列空时,消费者线程试图从队列里 take 元素,队列也会阻塞消费者线程,直到队列可用。

超时退出:当阻塞队列满时,队列会阻塞生产者线程一段时间,如果超过一定的时间,生产者线程就会退出。

  • 任务队列(workQueue)

一个阻塞队列,用来存储线程池等待执行的任务,均为线程安全,它包含以下 7 种类型:

  1. ArrayBlockingQueue:一个由数组结构组成的有界阻塞队列。初始化时指定容量大小,有利于防止资源耗尽;一旦指定大小就不能再变。采用FIFO方式存储元素。

默认情况下不保证线程公平的访问队列

公平访问队列是指阻塞的线程,可以按照阻塞的先后顺序访问队列,即先阻塞线程先访问队列。

非公平性是对先等待的线程是非公平的,当队列可用时,阻塞的线程都可以争夺访问队列的资格,有可能先阻塞的线程最后才访问 队列。

为了保证公平性,通常会降低吞吐量。

  1. LinkedBlockingQueue:一个由链表结构组成的阻塞队列,大小配置可选,如果初始化时指定了大小,那么它就是有边界的。不指定就无边界在未指明容量时,容量默认为 Integer.MAX_VALUE,线程池默认使用的任务队列。

使用无界队列后,当核心线程都繁忙时,后续任务可以无限加入队列,线程池中线程数不会超过核心线程数。可提高线程池吞吐量,但代价是牺牲内存空间,甚至会导致内存溢出

LinkedBlockingQueue 队列按 FIFO(先进先出)排序元素

LinkedBlockingQueue 内部使用两个独占锁来保证线程安全,其中写入锁用于控制添加元素的操作,取出锁用于控制取出元素的操作。新元素从队列尾部入队,取出元素则从队列头部移除,因此可以同时在队列头部和队列尾部并发地进行元素取出、添加操作

  1. SynchronousQueue:是一个特殊的队列,它的内部同时只能够容纳单个元素,也可以说不能储存元素的阻塞队列。

​ 如果该队列已有一个元素的话,试图向队列中插入(Put)一个新元素的线程将会阻塞,直到另一个线程将该元素从队列中取走(take)。

​ 同样,如果该队列为空,试图向队列中抽取一个元素的线程将会阻塞,直到另一个线程向队列中插入了一条新的元素。

  1. PriorityBlockingQueue:带优先级的阻塞队列(默认升序)。无边界队列,允许插入null 。必须实现Comparable接口,这样才能通过实现compareTo()方法进行排序。优先级最高的元素将始终排在队列的头部。

不会保证优先级一样的元素的排序,也不保证当前队列中除了优先级最高的元素以外的元素,随时处于正确排序的位置。

  1. DelayQueue:这是一种支持延时获取元素基于二叉堆实现的无界优先级阻塞队列,要求队列里的元素都实现java.util.concurrent.Delayed 接口 意思就是,在往DelayQueue队列中存入元素时,可以指定:多久才能从队列中获取当前这些元素,即,延时获取;

  1. LinkedTransferQueue:

由链表结构组成的无界阻塞队列,采用一种预占模式:消费者线程取元素时,如果队列不为空,则直接取走数据,若队列为空,那就生成一个节点(节点元素为null)入队,然后消费者线程被等待在这个节点上,后面生产者线程入队时发现有一个元素为null的节点,生产者线程就不入队了,直接就将元素填充到该节点,并唤醒该节点等待的线程,被唤醒的消费者线程取走元素。

相对于其他阻塞队列,LinkedTransferQueue多了tryTransfer和transfer方法。

(1)transfer方法

如果当前有消费者正在等待接收元素(消费者使用take()方法或带时间限制的poll()方法时),transfer方法可以把生产者传入的元素立刻transfer(传输)给消费者。

如果没有消费者在等待接收元素,transfer方法会将元素存放在队列的tail节点,并等到该元素被消费者消费了才返回。

(2)tryTransfer方法

tryTransfer方法是用来试探生产者传入的元素是否能直接传给消费者。如果没有消费者等待接收元素,则返回false。

对于带有时间限制的tryTransfer(E e,long timeout,TimeUnit unit)方法,试图把生产者传入的元素直接传给消费者,但是如果没有消费者消费该元素则等待指定的时间再返回,如果超时还没消费元素,则返回false,如果在时限内消费了元素,则返回true。

(3)区别

tryTransfer方法无论消费者是否接收,方法立即返回,而transfer方法是必须等到消费者消费了才返回。

  1. LinkedBlockingDeque:

LinkedBlockingDeque是一个由链表结构组成的双向阻塞队列(可以从队列的两端插入和移出元素,减少了一半的竞争)。

相比其他的阻塞队列,LinkedBlockingDeque多了

addFirst、offerFirst、peekFirst

addLast、offerLast、peekLast 等方法

以First单词结尾的方法,表示插入、获取(peek)或移除双端队列的第一个元素。

以Last单词结尾的方法,表示插入、获取(peek)或移除双端队列的最后一个元素。

插入方法add等同于addLast

移除方法remove等效于removeFirst,但是take方法却等同于takeFirst。

使用时还是用带有First和Last后缀的方法更清楚。

在初始化LinkedBlockingDeque时可以设置容量防止其过度膨胀。另外,双向阻塞队列可以运用在“工作窃取”模式中。

  • 线程工厂(threadFactory)

线程工厂指定创建线程的方式,需要实现 ThreadFactory 接口,并实现 newThread(Runnable r) 方法。该参数可以不用指定,Executors 框架已经为我们实现了一个默认的线程工厂:默认为正常优先级、非守护线程。

ThreadFactory(父级接口)

public interface ThreadFactory {

    /**
     * Constructs a new {@code Thread}.  Implementations may also initialize
     * priority, name, daemon status, {@code ThreadGroup}, etc.
     *
     * @param r a runnable to be executed by new thread instance
     * @return constructed thread, or {@code null} if the request to
     *         create a thread is rejected
     */
    Thread newThread(Runnable r);
}

DefaultThreadFactory(默认使用)

/**
     * The default thread factory
     */
    static class DefaultThreadFactory implements ThreadFactory {
        private static final AtomicInteger poolNumber = new AtomicInteger(1);
        private final ThreadGroup group;
        private final AtomicInteger threadNumber = new AtomicInteger(1);
        private final String namePrefix;

        DefaultThreadFactory() {
            SecurityManager s = System.getSecurityManager();
            group = (s != null) ? s.getThreadGroup() :
                                  Thread.currentThread().getThreadGroup();
            namePrefix = "pool-" +
                          poolNumber.getAndIncrement() +
                         "-thread-";
        }
  • 拒绝策略(handler)

当线程池的线程数达到最大线程数时,需要执行拒绝策略。拒绝策略需要实现 RejectedExecutionHandler 接口,并实现 rejectedExecution(Runnable r, ThreadPoolExecutor executor) 方法。不过 Executors 框架已经为我们实现了 4 种拒绝策略:

AbortPolicy(默认):当任务添加到线程池中被拒绝时,它将抛出 RejectedExecutionException 异常。(该策略下,直接丢弃任务,并抛出RejectedExecutionException异常)

CallerRunsPolicy:不进入线程池执行,在这种方式(CallerRunsPolicy)中,任务将由调用者线程去执行。(用于被拒绝任务的处理程序,它直接在 execute 方法的调用线程中运行被拒绝的任务;如果执行程序已关闭,则会丢弃该任务。)

DiscardPolicy:当任务添加到线程池中被拒绝时,默认情况下它将丢弃被拒绝的任务。(即该策略下,直接丢弃任务,什么都不做)。

DiscardOldestPolicy:当任务添加到线程池中被拒绝时,线程池会放弃等待队列中最旧的未处理任务,然后将被拒绝的任务添加到等待队列中。(该策略下,抛弃进入队列最早的那个任务,然后尝试把这次拒绝的任务放入队列)

  1. 举例理解工作原理(工作流程)

2.1、工作原理流程图

2.2、以现实中去银行办理业务流程为例

客户A(任务)去银行(线程池)办理业务,但银行刚开门,窗口服务人员还没有就位(相当于线程池里初始线程个数为0),于是经理(线程池管理者)就安排1号员工(创建一号线程执行任务)接待A客户(创建线程),在A客户还没有办理完业务时,B客户(任务)又来了,于是经理(线程池管理者)就安排2号员工(创建二号线程执行任务)接待B客户(创建了一个新的线程)。

假设银行只有两个窗口(核心线程数量就是2)

紧接着在A、B两位客户没有完成业务的情况下,C客户(新的任务),经理就把C客户安排到大厅里的等候区(任务队列)的椅子(椅子的数量就是队列的容量)上坐下,并告知他,如果1、2两个窗口有空出来的,C客户就可以去空出来的那个窗口办理业务。

此时又来了一位D客户,现在情况就是窗口满人,等候区也满人(就是核心线程都被占用,任务队列也满了),这时候经理就会让临时员工手持平白电脑(创建三号线程执行任务)给D客户办理业务,

假如前面的业务都没有结束,又来了E客户,此时正式员工,临时员工都在忙(临时员工的数量加上正式员工的数量就是线程池最大线程数量),等候区也满了,于是经理就只能按照《超出银行最大接待能力,处理办法》(拒绝策略)拒绝接待E客户(也就是线程池默认的拒绝策略AbortPolicy(默认):丢弃任务并抛出 RejectedExecutionException 异常。)

最后进来办理业务的客户少了,大厅里的临时工也空闲了一个小时了(最大空闲时间),经理就会让临时员工下班(销毁线程),但是为了保证银行能正常运行(变量allowCoreThreadTimeout可以控制是否允许销毁核心线程默认值为false,是不销毁 ),即使正式员工闲着,也不得提前下班(池内保持核心线程数量)

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值