• Java并发之ThreadPoolExecutor


    ThreadPoolExecutor源码分析

    简介

    java.uitl.concurrent.ThreadPoolExecutor类是线程池中最核心的一个类,因此如果要透彻地了解Java中的线程池,必须先了解这个类。本文就从源码着手开始一步步了解ThreadPoolExecutor

    类图

    在这里插入图片描述

    ThreadPoolExecutor继承了AbstractExecutorService类,并提供了四个构造器,通过观察每个构造器的源码,发现前面三个构造器都是调用的第四个构造器进行的初始化工作

    
        public ThreadPoolExecutor(int corePoolSize,
                                  int maximumPoolSize,
                                  long keepAliveTime,
                                  TimeUnit unit,
                                  BlockingQueue<Runnable> workQueue) {}
    
        
        public ThreadPoolExecutor(int corePoolSize,
                                  int maximumPoolSize,
                                  long keepAliveTime,
                                  TimeUnit unit,
                                  BlockingQueue<Runnable> workQueue,
                                  ThreadFactory threadFactory) {}
    
        
        public ThreadPoolExecutor(int corePoolSize,
                                  int maximumPoolSize,
                                  long keepAliveTime,
                                  TimeUnit unit,
                                  BlockingQueue<Runnable> workQueue,
                                  RejectedExecutionHandler handler) {}
    
       
        public ThreadPoolExecutor(int corePoolSize,
                                  int maximumPoolSize,
                                  long keepAliveTime,
                                  TimeUnit unit,
                                  BlockingQueue<Runnable> workQueue,
                                  ThreadFactory threadFactory,
                                  RejectedExecutionHandler handler) {}
    

    构造函数中各个参数含义如下:

    corePoolSize:核心线程池大小
    默认情况下,在创建了线程池后,池中的线程数为0,当有任务来了之后就会创建线程去执行任务,当线程池中,线程的数量超过corePoolSize后,就会把新的任务放在缓存队列中
    
    maxinumPoolSize:线程池所能容纳的最大线程数
    超过这个数的线程将被阻塞。当任务队列为没有设置大小的LinkedBlockingDeque时,这个值无效
    
    keepAliveTime:非核心线程的闲置超时时间,超过这个时间就会被回收
    当线程中没有可执行的任务时,最多保持多长时间会停止,默认情况下只有线程池中线程数量大于corePoolSize,该参数才会起作用。但是如果调用了allowCoreThreadTimeOut(boolean)方法,当线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0
    
    unit:指定keepAliveTime的单位,如TimeUnit.SECONDS。当将allowCoreThreadTimeOut设置为true时对corePoolSize生效
    
    workQueue:阻塞队列
    用来保存等待执行的任务
    
    threadFactory:线程工厂
    用来创建线程
    
    handler:拒绝处理任务的策略
    ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。 
    ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常。 
    ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
    ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务
    
    

    ThreadPoolExecutor重要方法

    execute():
    该方法是Executor中声明的方法,在ThreadPoolExecutor进行了具体的实现,这个方法是ThreadPoolExecutor的核心方法,通过这个方法可以向线程池提交一个任务,交由线程池去执行
    
    submit():
    在ExecutorService中声明的方法,在AbstractExecutorService有了具体的实现,在ThreadPoolExecutor中并没有对其进行重写,这个方法也是用来向线程池提交任务的,但是它和execute()方法不同,它能够返回任务执行的结果,去看submit()方法的实现,会发现它实际上还是调用的execute()方法,只不过它利用了Future来获取任务执行结果
    
    shutdown():
    关闭线程池,前提等线程池中正在执行的任务执行完毕再关闭
    
    shutdownNow()
    立即关闭线程池,会强制终止正在执行任务的线程
    

    继承关系

    从源码中看到ThreadPoolExecutor继承于AbstractExecutorService,我们看一下这个抽象类的源码发现AbstractExecutorService是一个抽象类,实现了ExecutorService接口

    public abstract class AbstractExecutorService implements ExecutorService {
     
        protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) { };
        protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) { };
        public Future<?> submit(Runnable task) {};
        public <T> Future<T> submit(Runnable task, T result) { };
        public <T> Future<T> submit(Callable<T> task) { };
        private <T> T doInvokeAny(Collection<? extends Callable<T>> tasks,
                                boolean timed, long nanos)
            throws InterruptedException, ExecutionException, TimeoutException {
        };
        public <T> T invokeAny(Collection<? extends Callable<T>> tasks)
            throws InterruptedException, ExecutionException {
        };
        public <T> T invokeAny(Collection<? extends Callable<T>> tasks,
                               long timeout, TimeUnit unit)
            throws InterruptedException, ExecutionException, TimeoutException {
        };
        public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
            throws InterruptedException {
        };
        public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
                                             long timeout, TimeUnit unit)
            throws InterruptedException {
        };
    }
    

    接着看接口ExecutorService源码发现,它继承了接口Excetor

    public interface ExecutorService extends Executor {
     
        void shutdown();
        boolean isShutdown();
        boolean isTerminated();
        boolean awaitTermination(long timeout, TimeUnit unit)
            throws InterruptedException;
        <T> Future<T> submit(Callable<T> task);
        <T> Future<T> submit(Runnable task, T result);
        Future<?> submit(Runnable task);
        <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
            throws InterruptedException;
        <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
                                      long timeout, TimeUnit unit)
            throws InterruptedException;
     
        <T> T invokeAny(Collection<? extends Callable<T>> tasks)
            throws InterruptedException, ExecutionException;
        <T> T invokeAny(Collection<? extends Callable<T>> tasks,
                        long timeout, TimeUnit unit)
            throws InterruptedException, ExecutionException, TimeoutException;
    }
    

    再来看接口Executor,它只有一个方法,执行传入的任务

    public interface Executor {
        void execute(Runnable command);
    }
    

    总结:

    Executor是一个顶层接口,在它里面只声明了一个方法execute(Runnable),返回值为void,参数为Runnable类型,用来执行传进去的任务的;

    ExecutorService接口继承了Executor接口,并声明了一些方法:submit、invokeAll、invokeAny以及shutDown等;

    AbstractExecutorService实现了ExecutorService接口,基本实现了ExecutorService中声明的所有方法;

    然后ThreadPoolExecutor继承了类AbstractExecutorService。

    ThreadPoolExecutor实现原理

    1.线程池状态

    //正在运行,接受新政务并且处理队列中的任务
    private static final int RUNNING    = -1 << COUNT_BITS;
    //关闭状态,不接受新任务,但是处理队列中的任务
    private static final int SHUTDOWN   =  0 << COUNT_BITS;
    //停止状态,既不接受新任务也不处理队列中的任务,并且终止正在执行任务的线程
    private static final int STOP       =  1 << COUNT_BITS;
    //所有任务都终止了,workerCount为零,线程过渡到状态清理,将运行terminated() 终止方法
    private static final int TIDYING    =  2 << COUNT_BITS;
    //完全终止状态
    private static final int TERMINATED =  3 << COUNT_BITS;
    

    状态转换条件:

    RUNNING -> SHUTDOWN
    On invocation of shutdown(), perhaps implicitly in finalize()
    
    (RUNNING or SHUTDOWN) -> STOP
    On invocation of shutdownNow()
    
    SHUTDOWN -> TIDYING
    When both queue and pool are empty
    
    STOP -> TIDYING
    When pool is empty
    
    TIDYING -> TERMINATED
    When the terminated() hook method has completed
    

    2.任务的执行

    public void execute(Runnable command) {
            if (command == null)
                throw new NullPointerException();
            int c = ctl.get();
            //正在工作的线程数量小于线程池设定的数量,将会启动一个新线程执行任务
            if (workerCountOf(c) < corePoolSize) {
                if (addWorker(command, true))
                    return;
                c = ctl.get();
            }
            //如果当前线程池处于RUNNING状态且将任务放入任务缓存队列成功,则继续进行判断
            if (isRunning(c) && workQueue.offer(command)) {
                int recheck = ctl.get();
                //如果当前线程池不是UNNING状态并且从队列中移除任务失败,将拒绝接受该任务
                if (! isRunning(recheck) && remove(command))
                    reject(command);
                else if (workerCountOf(recheck) == 0)
                    addWorker(null, false);
            }
            else if (!addWorker(command, false))
                reject(command);
        }
    

    如果当前线程池中的线程数目小于corePoolSize,则每来一个任务,就会创建一个线程去执行这个任务;

    如果当前线程池中的线程数目>=corePoolSize,则每来一个任务,会尝试将其添加到任务缓存队列当中,若添加成功,则该任务会等待空闲线程将其取出去执行;若添加失败(一般来说是任务缓存队列已满),则会尝试创建新的线程去执行这个任务;

    如果当前线程池中的线程数目达到maximumPoolSize,则会采取任务拒绝策略进行处理

    如果线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止,直至线程池中的线程数目不大于corePoolSize;如果允许为核心池中的线程设置存活时间,那么核心池中的线程空闲时间超过keepAliveTime,线程也会被终止

    3.线程池中的线程初始化

    默认情况下,创建线程池之后,线程池中是没有线程的,需要提交任务之后才会创建线程。

    在实际中如果需要线程池创建之后立即创建线程,可以通过以下两个方法办到:

    prestartCoreThread():初始化一个核心线程;
    
    prestartAllCoreThreads():初始化所有核心线程
    
    public boolean prestartCoreThread() {
            return workerCountOf(ctl.get()) < corePoolSize &&
                addWorker(null, true);
        }
        
    public int prestartAllCoreThreads() {
            int n = 0;
            while (addWorker(null, true))
                ++n;
            return n;
        }
    

    注意上面传进去的参数是null,最后线程会阻塞在addWorker,等待任务队列中有任务

    4.任务缓存队列及排队策略

    在前面我们多次提到了任务缓存队列,即workQueue,它用来存放等待执行的任务。

    workQueue的类型为BlockingQueue,通常可以取下面三种类型:

    1)ArrayBlockingQueue:基于数组的先进先出队列,此队列创建时必须指定大小;
    
    2)LinkedBlockingQueue:基于链表的先进先出队列,如果创建时没有指定此队列大小,则默认为Integer.MAX_VALUE;
    
    3)synchronousQueue:这个队列比较特殊,它不会保存提交的任务,而是将直接新建一个线程来执行新来的任务。
    

    5.任务拒绝策略

    当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize,如果还有任务到来就会采取任务拒绝策略,通常有以下四种策略:

    ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
    ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常。
    ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
    ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务
    

    6.线程池的关闭

    ThreadPoolExecutor提供了两个方法,用于线程池的关闭,分别是shutdown()和shutdownNow(),其中:

    shutdown():不会立即终止线程池,而是要等所有任务缓存队列中的任务都执行完后才终止,但再也不会接受新的任务
    shutdownNow():立即终止线程池,并尝试打断正在执行的任务,并且清空任务缓存队列,返回尚未执行的任务
    

    7.线程池容量的动态调整

    ThreadPoolExecutor提供了动态调整线程池容量大小的方法:setCorePoolSize()和setMaximumPoolSize(),

    setCorePoolSize:设置核心池大小
    setMaximumPoolSize:设置线程池最大能创建的线程数目大小
    

    线程池规则

    线程池的线程执行规则跟任务队列有很大的关系。

    下面都假设任务队列没有大小限制:

    • 如果线程数量<=核心线程数量,那么直接启动一个核心线程来执行任务,不会放入队列中。
    • 如果线程数量>核心线程数,但<=最大线程数,并且任务队列是LinkedBlockingDeque的时候,超过核心线程数量的任务会放在任务队列中排队。
    • 如果线程数量>核心线程数,但<=最大线程数,并且任务队列是SynchronousQueue的时候,线程池会创建新线程执行任务,这些任务也不会被放在任务队列中。这些线程属于非核心线程,在任务完成后,闲置时间达到了超时时间就会被清除。
    • 如果线程数量>核心线程数,并且>最大线程数,当任务队列是LinkedBlockingDeque,会将超过核心线程的任务放在任务队列中排队。也就是当任务队列是LinkedBlockingDeque并且没有大小限制时,线程池的最大线程数设置是无效的,他的线程数最多不会超过核心线程数。
    • 如果线程数量>核心线程数,并且>最大线程数,当任务队列是SynchronousQueue的时候,会因为线程池拒绝添加任务而抛出异常。

    任务队列大小有限时

    • 当LinkedBlockingDeque塞满时,新增的任务会直接创建新线程来执行,当创建的线程数量超过最大线程数量时会抛异常。
    • SynchronousQueue没有数量限制。因为他根本不保持这些任务,而是直接交给线程池去执行。当任务数量超过最大线程数时会直接抛异常。

    事例

    public class ThreadPoolExecutorTest {
        public static void main(String[] args) {
            ThreadPoolExecutor executor = new ThreadPoolExecutor(5,
                    10, 200, TimeUnit.MILLISECONDS,
                    new ArrayBlockingQueue<Runnable>(5));
    
            for(int i=0;i<10;i++){
                MyTask myTask = new MyTask(i);
                executor.execute(myTask);
                System.out.println("线程池中线程数目:"+executor.getPoolSize()+",队列中等待执行的任务数目:"+
                        executor.getQueue().size()+",已执行完成的任务数目:"+executor.getCompletedTaskCount());
            }
            executor.shutdown();
        }
        static class MyTask implements Runnable {
            private int taskNum;
    
            public MyTask(int num) {
                this.taskNum = num;
            }
    
            @Override
            public void run() {
                System.out.println("正在执行task "+taskNum);
                try {
                    sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("task "+taskNum+"执行完毕");
            }
        }
    }
    
    
    博主文章同步发布:https://blog.csdn.net/u010647035/article/details/83959552
  • 相关阅读:
    火狐浏览器插件开发工具
    MyEclipse设置JSP页面默认编码方式
    如何让editplus保存时不生成.bak备份文件
    Sublime Text 2 使用心得
    火狐浏览器的一些常用设置
    能帮你提高工作效率的10个在线工具
    如何治疗颈椎病
    企业架构研究总结(2)——问题的由来和基本概念
    企业架构研究总结(1)——参考资料列表
    企业架构研究总结(4)——企业架构与企业架构框架概论
  • 原文地址:https://www.cnblogs.com/liukaifeng/p/10052670.html
Copyright © 2020-2023  润新知