Java线程池解析(二)

📔上一篇文章介绍了线程池的基础知识,这节将更加深入;对于上一篇重复过的知识,这里不再赘述!

👟 ThreadPoolExecutor

再看继承结构:

线程池状态

上一节中已经阐述了线程池的五种状态

这对应于源码中的:

    
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;      // 29
private static final int CAPACITY   = (1 << COUNT_BITS) - 1; // 00011111 ... ... 11111111

// 状态在高位存储:RUNNING算起来较复杂,注意负数的补码=反码+1
private static final int RUNNING    = -1 << COUNT_BITS;      // 11100000 ... ... 00000000
private static final int SHUTDOWN   =  0 << COUNT_BITS;      // 00000000 ... ... 00000000
private static final int STOP       =  1 << COUNT_BITS;      // 00100000 ... ... 00000000
private static final int TIDYING    =  2 << COUNT_BITS;      // 01000000 ... ... 00000000
private static final int TERMINATED =  3 << COUNT_BITS;      // 01100000 ... ... 00000000
    // Packing and unpacking ctl
    private static int runStateOf(int c)     { return c & ~CAPACITY; }
    private static int workerCountOf(int c)  { return c & CAPACITY; }
	// rs 为高 3 位代表线程池状态, wc 为低 29 位代表线程个数,ctl 是合并它们
    private static int ctlOf(int rs, int wc) { return rs | wc; }

    /*
     * Bit field accessors that don't require unpacking ctl.
     * These depend on the bit layout and on workerCount being never negative.
     */

    private static boolean runStateLessThan(int c, int s) {
        return c < s;
    }

    private static boolean runStateAtLeast(int c, int s) {
        return c >= s;
    }

    private static boolean isRunning(int c) {
        return c < SHUTDOWN;
    }

这是ThreadPoolExecutor类开头的一段代码,看起来比较吃力。

其中的位运算,这里就不做具体验证了。

状态名 高 3位 接收新任务 处理阻塞队列任务 说明
RUNNING 111 Y Y
SHUTDOWN 000 N Y 不会接收新任务,但会处理阻塞队列剩余任务
STOP 001 N N 会中断正在执行的任务,并抛弃阻塞队列任务
TIDYING 010 - - 任务全执行完毕,活动线程为 0 即将进入终结
TERMINATED 011 - - 终结状态

从数字上比较,TERMINATED > TIDYING > STOP > SHUTDOWN > RUNNING

这些信息存储在一个原子变量 ctl 中,目的是将线程池状态与线程个数合二为一,这样就可以用一次 cas 原子操作进行赋值,更容易保证在多线程环境下保证运行状态和线程数量的统一

都是大师的智慧啊


🚒拒绝策略

JDK提供了4种拒绝策略的实现,其他框架也提供了实现。

  • AbortPolicy(抛出一个异常,默认的)
  • DiscardPolicy(直接丢弃任务)
  • DiscardOldestPolicy(丢弃队列里最老的任务,将当前这个任务继续提交给线程池)
  • CallerRunsPolicy(交给线程池调用所在的线程进行处理)
  • Dubbo 的实现,在抛出 RejectedExecutionException 异常之前会记录日志,并 dump 线程栈信息,方
    便定位问题
  • Netty 的实现,是创建一个新线程来执行任务
  • ActiveMQ 的实现,带超时等待(60s)尝试放入队列,类似我们之前自定义的拒绝策略
  • PinPoint 的实现,它使用了一个拒绝策略链,会逐一尝试策略链中每种拒绝策略

例如:AbortPolicy

    public static class AbortPolicy implements RejectedExecutionHandler {
        /**
         * Creates an {@code AbortPolicy}.
         */
        public AbortPolicy() { }

        /**
         * Always throws RejectedExecutionException.
         *
         * @param r the runnable task requested to be executed
         * @param e the executor attempting to execute this task
         * @throws RejectedExecutionException always
         */
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            throw new RejectedExecutionException("Task " + r.toString() +
                                                 " rejected from " +
                                                 e.toString());
        }
    }

其他可自行参阅ThreadPoolExecutor


浅谈:newSingleThreadExecutor

上一篇文章中,我们简单的介绍过newSingleThreadExecutor

这是JDK为我们内置的一个单线程的线程池

通过Executors工具类来创建

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

为啥返回的是FinalizableDelegatedExecutorService类呢???

考虑这样一个问题:

创建一个单线程的线程池与Executors.newFixedThreadPool(1)有什么区别呢?

👼

返回的FinalizableDelegatedExecutorService

    static class FinalizableDelegatedExecutorService
        extends DelegatedExecutorService {
        FinalizableDelegatedExecutorService(ExecutorService executor) {
            super(executor);
        }
        protected void finalize() {
            super.shutdown();
        }
    }
//继承自DelegatedExecutorService

    static class DelegatedExecutorService extends AbstractExecutorService {
        //持有一个线程池的引用
        private final ExecutorService e;
        DelegatedExecutorService(ExecutorService executor) { e = executor; }
        //可以看到调用的都是ExecutorService中的方法
        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);
        }
        public Future<?> submit(Runnable task) {
            return e.submit(task);
        }
        public <T> Future<T> submit(Callable<T> task) {
            return e.submit(task);
        }
        public <T> Future<T> submit(Runnable task, T result) {
            return e.submit(task, result);
        }
        public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
            throws InterruptedException {
            return e.invokeAll(tasks);
        }
        public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
                                             long timeout, TimeUnit unit)
            throws InterruptedException {
            return e.invokeAll(tasks, timeout, unit);
        }
        public <T> T invokeAny(Collection<? extends Callable<T>> tasks)
            throws InterruptedException, ExecutionException {
            return e.invokeAny(tasks);
        }
        public <T> T invokeAny(Collection<? extends Callable<T>> tasks,
                               long timeout, TimeUnit unit)
            throws InterruptedException, ExecutionException, TimeoutException {
            return e.invokeAny(tasks, timeout, unit);
        }
    }

这里可以看出, 这是一个典型的装饰者设计模式

其作用:其返回的FinalizableDelegatedExecutorService类,只能调用部分方法,即上图中的方法,

newFixedThreadPool返回的是ThreadPoolExecutor类。对比下就可以发现方法的数量不同。

比如:如果是Executors.newFixedThreadPool(1)

我们可以调用java.util.concurrent.ThreadPoolExecutor#setCorePoolSize这个方法改变线程池中线程的数量。

但是newSingleThreadExecutor没有这个方法,就保证了线程数量不会变更改。

既然是单个线程的线程池,为什么我们不走代码中直接创建一个新线程来执行任务呢,不也是串行的么?

答:自己创建一个单线程串行执行任务,如果任务执行失败而终止那么没有任何补救措施,而线程池还会新建一
个线程,保证池的正常工作

posted @ 2020-02-09 16:05  HeliusKing  阅读(167)  评论(0编辑  收藏  举报