美文网首页Android
android 线程池的理解

android 线程池的理解

作者: android源码探索 | 来源:发表于2020-02-13 17:53 被阅读0次

    在我看来 理解线程池是非常有必要的,像一些比较出名的开源 框架都用到了线程池, 比如: Okhttp, EventBus等

    对于我个人而言,平时对线程池的使用也只是停留在使用层面, 对其原理了解的少之又少,我自己也很好奇,于是花了点时间研究了一下。

    带着如下几个问题对线程池进行了解:

    1. ThreadPoolExecutor的工作状态
    2. ThreadPoolExecutor的重要成员变量
    3. 重要成员变量
    4. 常见的线程池

    里面大量用到了ReentrantLock, 以及CAS的相关知识, 这一块可以自己脑补一下

    ThreadPoolExecutor的工作状态

    线程池有5种状态

       private static final int RUNNING    = -1 << COUNT_BITS;
        private static final int SHUTDOWN   =  0 << COUNT_BITS;
        private static final int STOP       =  1 << COUNT_BITS;
        private static final int TIDYING    =  2 << COUNT_BITS;
        private static final int TERMINATED =  3 << COUNT_BITS;
    

    每一种状态的具体含义如下(对于这一块只需要记住即可):

    • RUNNING(运行,-1):能够接收新任务,也可以处理阻塞队列中的任务。
    • SHUTDOWN(待关闭,0):不可以接受新任务,继续处理阻塞队列中的任务。
    • STOP(停止,1):不接收新任务,不处理阻塞队列中的任务,并且会中断正在处理的任务。
    • TIDYING(整理,2):所有的任务已终止,ctl记录的“工作线程数”为0,线程池会变为TIDYING状态。当线程池变为TIDYING状态时,执行钩子方法terminated()。对于它的实现,在ThreadPoolExecutor中什么也没做。
    • TERMINATED(终止,3):完全终止,且完成了所有资源的释放。

    线程池的工作状态是什么时候切换呢

    • RUNNING -> SHUTDOWN:在调用ShutDown()时或隐式调用在finalize()中。
    • (RUNNING or SHUTDOWN) -> STOP:调用shutdownNow()时。
    • SHUTDOWN -> TIDYING:当阻塞和队列和工作线程数都为0时。
    • STOP -> TIDYING:当工作线程数为0。
    • TIDYING -> TERMINATED:当terminated()钩子方法完成时。

    通过源码来验证上面的说法:

    public void shutdown() {
            final ReentrantLock mainLock = this.mainLock;
            mainLock.lock();
            try {
                checkShutdownAccess();
                advanceRunState(SHUTDOWN);
                interruptIdleWorkers();
                onShutdown(); // hook for ScheduledThreadPoolExecutor
            } finally {
                mainLock.unlock();
            }
            tryTerminate();
        }
    

    从上面的源码里面看到了如下的一句代码advanceRunState(SHUTDOWN);

     private void advanceRunState(int targetState) {
            // assert targetState == SHUTDOWN || targetState == STOP;
            for (;;) {
                int c = ctl.get();
                if (runStateAtLeast(c, targetState) ||
                    ctl.compareAndSet(c, ctlOf(targetState, workerCountOf(c))))
                    break;
            }
        }
    

    这句代码ctl.compareAndSet(c, ctlOf(targetState, workerCountOf(c))))
    就是通过 CAS的原理进行值 的修改, 而ctlOf方法会计算出真正的状态值

    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.corePoolSize = corePoolSize;
            this.maximumPoolSize = maximumPoolSize;
            this.workQueue = workQueue;
            this.keepAliveTime = unit.toNanos(keepAliveTime);
            this.threadFactory = threadFactory;
            this.handler = handler;
        }
    
    • int corePoolSize--核心线程数
    • int maximumPoolSize--最大线程数
    • long keepAliveTime--允许线程空闲时间
    • TimeUnit unit--时间对象
    • BlockingQueue<Runnable> workQueue--阻塞队列
    • ThreadFactory threadFactory--线程工厂
    • RejectedExecutionHandler handler--任务拒绝策略

    ThreadFactory

    线程工厂
    其作用是创建线程
    如果外面不传入线程工厂, 则默认用源码里面的DefaultThreadFactory

    RejectedExecutionHandler

    拒绝策略
    是在线程池执行excute方法的时候, 如果满足一定条件则会执行其方法

    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();
            }
            if (isRunning(c) && workQueue.offer(command)) {
                int recheck = ctl.get();
                if (! isRunning(recheck) && remove(command))
                    reject(command);
                else if (workerCountOf(recheck) == 0)
                    addWorker(null, false);
            }
            else if (!addWorker(command, false))
                reject(command);
        }
    

    从上面的代码分析看到了如下的代码reject(command);
    而这个方法是真正的执行拒绝策略里面的方法。

    从源码分析 拒绝策略有如下几种:
    1)直接抛出异常(AbortPolicy)
    默认采用,对拒绝任务抛弃处理,并且抛出RejectedExecutionException异常。

     public static class AbortPolicy implements RejectedExecutionHandler {
            public AbortPolicy() { }
    
            public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
                throw new RejectedExecutionException("Task " + r.toString() +
                                                     " rejected from " +
                                                     e.toString());
            }
        }
    

    2)使用调用者的线程来处理(CallerRunsPolicy)
    如果当前线程池处于运行状态 ,直接使用当前线程执行任务,如果是终止状态,则直接抛弃。

    public static class CallerRunsPolicy implements RejectedExecutionHandler {
            
            public CallerRunsPolicy() { }
    
            public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
                if (!e.isShutdown()) {
                    r.run();
                }
            }
        }
    

    3)直接丢掉这个任务(DiscardPolicy)
    对拒绝任务偷偷地抛弃,没有异常信息。查看源码发现他的rejectedExecution ()就是一个空实现。

    public static class DiscardPolicy implements RejectedExecutionHandler {
          
            public DiscardPolicy() { }
    
            public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            }
        }
    

    4)丢掉最老的任务(DiscardOldestPolicy)
    对拒绝任务不抛弃,而是抛弃队列里面等待最久的一个任务,然后把拒绝任务加到队列。

    public static class DiscardOldestPolicy implements RejectedExecutionHandler {
          
            public DiscardOldestPolicy() { }
            public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
                if (!e.isShutdown()) {
                    e.getQueue().poll();
                    e.execute(r);
                }
            }
        }
    

    常见的线程池

    • FixedThreadPool,
    • CachedThreadPool,
    • SingleThreadPool,
    • ScheduledThreadPool,
    • ForkJoinThreadPool

    这些线程池的创建方法是借助于Excutor工具类来创建的,
    其不同之处就是线程池的构造方法的参数不同而已

    关联文章:
    OkHttp的主线流程详解

    关注公众号会不定期更新内容:

    qrcode_for_gh_c78cd816dc53_344.jpg

    相关文章

      网友评论

        本文标题:android 线程池的理解

        本文链接:https://www.haomeiwen.com/subject/ofylfhtx.html