美文网首页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线程池得要这么用 - 简书 线程池相关知识:Android开发之线程池使用总结 ...

  • android 线程池的理解

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

  • 万字长文:带你透彻理解“线程池”

    目标 【理解】线程池的基本概念 【理解】线程池工作原理 【掌握】自定义线程池 【应用】java内置线程池 【应用】...

  • Android线程池的使用

    一、线程与线程池,为什么要使用线程池 1、Android中的线程 在Android中有主线程和子线程的区分。主线程...

  • 线程池

    话题:线程池Android中的线程池有哪些?它们的区别是什么?为什么要使用线程池? 线程是Android里面一个很...

  • Android线程池

    上篇将android多线程篇幅过长,所以决定线程池这一张拆开来讲。其实在我的okhttp源码理解内已经谈过线程池的...

  • 笔记:Android线程和线程池

    Android线程和线程池 Android中的线程操作相关的类有 AsyncTask IntentService ...

  • 第十九周 线程池

    话题:线程池 Android 中的线程池有哪些?它们的区别是什么?为什么要使用线程池?关键字:线程池、Thread...

  • Android 关于线程池的理解

    前言 线程池是Java中的一个重要概念,从Android上来说,当我们跟服务端进行数据交互的时候我们都知道主线程不...

  • Android 多线程:线程池理解和使用总结

    一、Android线程池介绍 1.1 原理 Android中的线程池概念来源于Java中的Executor,Exe...

网友评论

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

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