【JavaEE】Executors中常见线程池工厂方法及其使用

参考文章:Java Executors类的9种创建线程池的方法及应用场景分析 - 威哥爱编程 - SegmentFault 思否

本文将介绍Executors中常见一些工厂方法。Executors中的工厂方法其实就是对ThreadPoolExecutor的封装,方便使用。


线程池的执行流程

  1. 创建线程池,并用线程工厂给线程起个好名字。
  2. 有任务过来,线程池才会创建线程,并执行任务。
  3. 后续还有任务过来,所有核心线程没有空闲的,那么就放入任务队列中等待,直到有空闲的线程。
  4. 任务队列如果是有界的,如果任务超过了队列的大小,此时就会创建临时线程来执行任务。
  5. 如果临时线程也到达了最大值,并且都没有空闲的。此时就会执行四种拒绝策略
    1. AbortPolicy 让调用者抛出 RejectedExecutionException 异常,这是默认策略
    2. CallerRunsPolicy 让调用者运行任务
    3. DiscardPolicy 放弃本次任务
    4. DiscardOldestPolicy 放弃队列中最早的任务,本任务取而代之
  6. 当任务都被完成后,临时线程到达指定的最大空闲时间后就会被释放。

可以看到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;
    }

提交任务

这里提交任务都是线程池中提交任务的方法。

        // 执行任务
        void execute(Runnable command);

        // 提交任务 task,用返回值 Future 获得任务执行结果
        <T> Future<T> submit(Callable<T> task);

        // 提交 tasks 中所有任务
        <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)throws InterruptedException;

        // 提交 tasks 中所有任务,带超时时间
        <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException;

        // 提交 tasks 中所有任务,哪个任务先成功执行完毕,返回此任务执行结果,其它任务取消
        <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException;

        // 提交 tasks 中所有任务,哪个任务先成功执行完毕,返回此任务执行结果,其它任务取消,带超时时间
        <T> T invokeAny(Collection<? extends Callable<T>> tasks,long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;

处理异常

线程池中的异常处理可以在线程执行内部捕捉,还可以用Futrue,在拿到结果后处理。

主动捕捉

        ExecutorService pool = Executors.newFixedThreadPool(2);
        pool.submit(() -> {
            try {
                System.out.println("hello");
                int i = 1 / 0;
            } catch (Exception e) {
                e.printStackTrace();
            }
        });

Futrue捕捉

        ExecutorService pool = Executors.newFixedThreadPool(1);
        Future<Boolean> f = pool.submit(() -> {
            System.out.println("hello");
            int i = 1 / 0;
            System.out.println("world");
            return true;
        });
        try {
            f.get();
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }

newFixedThreadPool

创建固定大小的线程池。

这里核心线程数==最大线程数,也就是说不会创建临时线程,所以也不需要超时时间。

工作队列是LinkedBlockingQueue,可以看成是无界的(实际上是int的最大值)可以看到下面的源码。

    public LinkedBlockingQueue(int capacity) {
        if (capacity <= 0) throw new IllegalArgumentException();
        this.capacity = capacity;
        last = head = new Node<E>(null);
    }

    /**
     * Creates a {@code LinkedBlockingQueue} with a capacity of
     * {@link Integer#MAX_VALUE}, initially containing the elements of the
     * given collection,
     * added in traversal order of the collection's iterator.
     *
     * @param c the collection of elements to initially contain
     * @throws NullPointerException if the specified collection or any
     *         of its elements are null
     */
    public LinkedBlockingQueue(Collection<? extends E> c) {
        this(Integer.MAX_VALUE);
        final ReentrantLock putLock = this.putLock;
        putLock.lock(); // Never contended, but necessary for visibility
        try {
            int n = 0;
            for (E e : c) {
                if (e == null)
                    throw new NullPointerException();
                if (n == capacity)
                    throw new IllegalStateException("Queue full");
                enqueue(new Node<E>(e));
                ++n;
            }
            count.set(n);
        } finally {
            putLock.unlock();
        }
    }

 

    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

该线程池适合运用于任务量已知,相对来说比较耗时的工作。因为它可以保证任务以固定的线程数量并行执行,同时避免了线程数量的无限制增长。然而,由于线程池的大小是固定的,如果任务提交的速率超过了线程池的处理能力,可能会导致任务在队列中等待较长时间。因此,在使用 newFixedThreadPool 时,需要根据任务的特性和预期的负载来合理设置 nThreads 的值。


newCachedThreadPool

创建临时线程干活。

核心线程数是0,最大线程数是int的最大值 --> 临时线程数是int的最大值

临时线程最大的空闲时间是60秒。

工作队列是SynchronousQueue,其特点是没有容量,没有线程来取任务是放不进取任务的。

    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

这种设计使得 newCachedThreadPool 非常适合处理大量短生命周期的任务,因为它可以动态地调整线程数量以适应任务负载的变化。然而,由于它可以创建无限多的线程,如果没有适当的任务队列来控制任务的数量,可能会导致资源耗尽。因此,在使用 newCachedThreadPool 时,需要谨慎考虑任务的特性和系统的资源限制。

适用于执行大量短期异步任务,尤其是任务执行时间不确定的情况。例如,Web服务器处理大量并发请求,或者异步日志记录。


newSingleThreadExecutor

创建一个固定线程为1的线程池。

希望多个任务排队执行。线程数固定为 1,任务数多于 1 时,会放入无界队列排队。任务执行完毕,这唯一的线程也不会被释放。

    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

与单独创建一个线程执行任务的区别:

自己创建一个单线程串行执行任务,如果任务执行失败而终止那么没有任何补救措施,而线程池还 会新建一 个线程,保证池的正常工作。
与Executors.newFixedThreadPool(1)的区别:
Executors.newSingleThreadExecutor() 线程个数始终为1,不能修改。FinalizableDelegatedExecutorService 应用的是装饰器模式,只对外暴露了 ExecutorService 接口,因此不能调用 ThreadPoolExecutor 中特有的方法
Executors.newSingleThreadExecutor() 线程个数始终为1,不能修改。FinalizableDelegatedExecutorService 应用的是装饰器模式,只对外暴露了 ExecutorService 接口,因此不能调用 ThreadPoolExecutor 中特有的方法

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值