Android开发中线程池源码解析

线程池(英语:thread pool):一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的数量。 例如,线程数一般取cpu数量+2比较合适,线程数过多会导致额外的线程切换开销。----摘自维基百科

我们在Android或者Java开发中,日常所使用的就是ThreadPoolExecutor了,我们先来看下如何使用一个线程池来代替多线程开发。

使用线程池

// 创建一个核心线程数为5,最大线程数为10,空闲线程存活时间为60s的线程池对象
val threadPoolExecutor = ThreadPoolExecutor(
    5, 10, 60,
    TimeUnit.MINUTES,
    ArrayBlockingQueue<Runnable>(100),
    RejectedExecutionHandler { _, _ -> println("reject submit thread to thread pool") }
)

// 测试
for (i in 1..10) {
    threadPoolExecutor.execute { println("execute thread is:${Thread.currentThread().name}") }
}

// 结果
// execute thread is:pool-1-thread-1
// execute thread is:pool-1-thread-1
// execute thread is:pool-1-thread-1
// execute thread is:pool-1-thread-1
// execute thread is:pool-1-thread-5
// execute thread is:pool-1-thread-5
// execute thread is:pool-1-thread-4
// execute thread is:pool-1-thread-3
// execute thread is:pool-1-thread-2
// execute thread is:pool-1-thread-1

从结果就可以看出来,执行时间操作,但是只创建了5个线程,另外5次都是复用线程的。这样就达到了复用存在的线程、减少对象的创建和销毁的额外开销;并且可以控制最大线程数,也就是控制了最大并发数。

知道如何使用一个线程池还不够,我们需要看看ThreadPoolExecutor是如何创建、复用这些线程的。下面我们看看创建ThreadPoolExecutor对象的几个参数:

构造方法

/**
     * 创建一个ThreadPoolExecutor对象
     *
     * @param corePoolSize 核心线程数,这些线程会一直在线程池中,除非设置了 allowCoreThreadTimeOut
     * @param maximumPoolSize 最大线程数,运行线程创建的最大值
     * @param keepAliveTime 当线程数>核心线程数的时候,这个值就是空闲且非核心线程存活的时间
     * @param unit keepAliveTime的单位
     * @param workQueue 保存task的队列,直到执行execute()方法执行
     * @param threadFactory ThreadFactory是一个接口,里面只有Thread newThread(Runnable r)方法,用来创建线程,
     *                      默认采用Executors.defaultThreadFactory()
     * @param handler 拒绝处理任务时的策略,如果线程池满了且所有线程都不处于空闲状态,
     *                通过RejectedExecutionHandler接口的rejectedExecution(Runnable r, ThreadPoolExecutor executor)来处理传进来的Runnable
     *                系统提供了四种:CallerRunsPolicy(), AbortPolicy(), DiscardPolicy(), DiscardOldestPolicy()
     *                默认采用new AbortPolicy()
     */
    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;
    }

我在方法头注释中我都一一解释了几个参数的作用,还有几点需要注意的就是:

  • 核心线程数不能小于0;
  • 最大线程数不能小于0;
  • 最大线程数不能小于核心线程数;
  • 空闲线程的存活时间不能小于0;

通过上面的解释我们很明白的知道前面几个参数的作用,但是最后两个参数我们并不能通过表面的解释通晓它,既然不能通过表象看懂他俩,那就看看默认的实现是如何做的,这样在接下来的源码分析中很有帮助。

ThreadFactory:线程工厂

ThreadFactory 是一个接口,里面只由唯一的 Thread newThread(Runnable r); 方法,此方法是用来创建线程的,从接口中我们得到的就只有这么多,下面我们看看 Executors 默认的 DefaultThreadFactory 类:

// 静态内部类
static class DefaultThreadFactory implements ThreadFactory {
    // 线程池的标识,从1开始没创建一个线程池+1
    private static final AtomicInteger poolNumber = new AtomicInteger(1);
    // 线程组
    private final ThreadGroup group;
    // 线程名中的结尾标识,从1开始每创建一个线程+1
    private final AtomicInteger threadNumber = new AtomicInteger(1);
    // 线程名
    private final String namePrefix;

    DefaultThreadFactory() {
        SecurityManager s = System.getSecurityManager();
        group = (s != null) ? s.getThreadGroup() :
                              Thread.currentThread().getThreadGroup();
        namePrefix = "pool-" +
                      poolNumber.getAndIncrement() +
                     "-thread-";
    }

    public Thread newThread(Runnable r) {
        Thread t = new Thread(group, r,
                              namePrefix + threadNumber.getAndIncrement(),
                              0);
        if (t.isDaemon())
            t.setDaemon(false);
        if (t.getPriority() != Thread.NORM_PRIORITY)
            t.setPriority(Thread.NORM_PRIORITY);
        return t;
    }
}

RejectedExecutionHandler:拒绝处理任务的策略

RejectedExecutionHandler 也是一个接口,并且也只提供了唯一的 void rejectedExecution(Runnable r, ThreadPoolExecutor executor); 方法。我们可以自定义策略,也可以用上面提到的封装好的四种策略,先看一下四种策略分别怎么拒绝任务的:

CallerRunsPolicy

public static class CallerRunsPolicy implements RejectedExecutionHandler {
    /**
     * Creates a {@code CallerRunsPolicy}.
     */
    public CallerRunsPolicy() {
    }

    /**
     * 如果线程池还没关闭,那么就再次执行这个Runnable
     */
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        if (!e.isShutdown()) {
            r.run();
        }
    }
}

AbortPolicy

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

    /**
     * 这个策略就是抛出异常,不做其他处理
     */
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        throw new RejectedExecutionException("Task " + r.toString() +
                " rejected from " +
                e.toString());
    }
}

DiscardPolicy

public static class DiscardPolicy implements RejectedExecutionHandler {
    /**
     * Creates a {@code DiscardPolicy}.
     */
    public DiscardPolicy() {
    }

    /**
     * 什么也不做,也就是抛弃了这个Runnable
     */
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    }
}

DiscardOldestPolicy

public static class DiscardOldestPolicy implements RejectedExecutionHandler {
    /**
     * Creates a {@code DiscardOldestPolicy} for the given executor.
     */
    public DiscardOldestPolicy() {
    }

    /**
     * 1. 线程池未关闭
     * 2. 获取队列中的下一个Runnable
     * 3. 获取到了,但是不对它进行处理,也就是抛弃它
     * 4. 执行我们传过来的这个Runnable
     */
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        if (!e.isShutdown()) {
            e.getQueue().poll();
            e.execute(r);
        }
    }
}

重要的参数

除了上述构造方法中的几个参数外,线程池还有几个比较核心的参数,如下:

public class ThreadPoolExecutor extends AbstractExecutorService {

    // ctl 的低29位表示线程池中的线程数,高3位表示当前线程状态
    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    // 29
    private static final int COUNT_BITS = Integer.SIZE - 3;
    // (2^29) -1
    private static final int CAPACITY   = (1 << COUNT_BITS) - 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为零,线程将运行terminate()方法
    private static final int TIDYING    =  2 << COUNT_BITS;
    // 终止状态:terminate()方法执行完成
    private static final int TERMINATED =  3 << COUNT_BITS;

    // 表示线程是否允许或停止
    private static int runStateOf(int c)     { return c & ~CAPACITY; }
    // 线程的有效数量
    private static int workerCountOf(int c)  { return c & CAPACITY; }
    private static int ctlOf(int rs, int wc) { return rs | wc; }

    ......后面的源码暂时省略
}

execute:执行

public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException();
    int c = ctl.get();
    // 如果运行中的线程数小于核心线程数,执行addWorker(command, true)创建新的核心Thread执行任务
    if (workerCountOf(c) < corePoolSize) {
        if (addWorker(command, true))
            return;
        c = ctl.get();
    }
    // 1. 已经满足:运行中的线程数大于核心线程数,但是小于最大线程数
    // 2. 需要满足:线程池在运行状态
    // 3. 需要满足:添加到工作队列中成功
    if (isRunning(c) && workQueue.offer(command)) {
        int recheck = ctl.get();
        // 如果线程不在运行状态,就从工作队列中移除command
        // 并且执行拒绝策略
        if (!isRunning(recheck) && remove(command))
            reject(command);
        // 线程池处于运行状态,但是没有线程,则addWorker(null, false)
        // 至于这里为什么要传入一个null,因为在最外层的if条件中我们已经将Runnable添加到工作队列中了
        // 而且在runWorker()源码中也可以得到答案,如果传入的Runnable为空,就会去工作队列中取task。
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false);
    }
    // 执行addWorker()创建新的非核心线程Thread执行任务
    // addWorker() 失败,执行拒绝策略
    else if (!addWorker(command, false))
        reject(command);
}

从上面源码中可以看出,execute()一个新的任务,主要有以下这几种情况:

1、核心线程未满,直接新建核心线程并执行任务;
2、核心线程满了,工作队列未满,将任务添加到工作队列中;
3、核心线程和工作队列都满,但是最大线程数未达到,新建线程并执行任务;
4、上面条件都不满足,那么就执行拒绝策略。

更形象的可以看下方流程图:

添加任务的流程图

addWorker(Runnable , boolean):添加Worker

private boolean addWorker(Runnable firstTask, boolean core) {
    // 标记外循环,比如在内循环中break retry就直接跳出外循环
    retry:
    for (; ; ) {
        int c = ctl.get();
        int rs = runStateOf(c);

        // 直接返回false有以下3种情况:
        // 1. 线程池状态为STOP、TIDYING、TERMINATED
        // 2. 线程池状态不是running状态,并且firstTask不为空
        // 3. 线程池状态不是running状态,并且工作队列为空
        if (rs >= SHUTDOWN &&
                !(rs == SHUTDOWN && firstTask == null && !workQueue.isEmpty()))
            return false;

        for (; ; ) {
            int wc = workerCountOf(c);
            // 如果添加的是核心线程,但是运行的线程数大于等于核心线程数,那么就不添加了,直接返回
            // 如果添加的是非核心线程,但是运行的线程数大于等于最大线程数,那么也不添加,直接返回
            if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                return false;
            // 增加workerCount的值 +1
            if (compareAndIncrementWorkerCount(c))
                // 跳出外循环
                break retry;
            c = ctl.get();  // 重新检查线程池状态
            if (runStateOf(c) != rs)
                continue retry;
            // 重新检查的状态和之前不合,再次从外循环进入
        }
    }

    boolean workerStarted = false;
    boolean workerAdded = false;
    Worker w = null;
    try {
        w = new Worker(firstTask);
        final Thread t = w.thread;
        if (t != null) {
            // 线程池重入锁
            final ReentrantLock mainLock = this.mainLock;
            // 获得锁
            mainLock.lock();
            try {
                // Recheck while holding lock.
                // Back out on ThreadFactory failure or if
                // shut down before lock acquired.
                int rs = runStateOf(ctl.get());
                // 线程池在运行状态或者是线程池关闭同时Runnable也为空
                if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) {
                    if (t.isAlive()) // precheck that t is startable
                        throw new IllegalThreadStateException();
                    // 想Worker中添加新的Worker
                    workers.add(w);
                    int s = workers.size();
                    if (s > largestPoolSize)
                        largestPoolSize = s;
                    workerAdded = true;
                }
            } finally {
                // 释放锁
                mainLock.unlock();
            }
            // 如果添加成功,启动线程
            if (workerAdded) {
                t.start();
                workerStarted = true;
            }
        }
    } finally {
        if (!workerStarted)
            addWorkerFailed(w);
    }
    return workerStarted;
}

addWorker() 主要就是在满足种种条件(上述源码中解释了)后,新建一个Worker对象,并添加到HashSet<Worker> workers中去,最后调用新建Worker对象的Thread变量的start()方法。

Worker类

Worker是一个继承了AQS并实现了Runnable的内部类,我们重点看看它的run()方法,因为上面addWorker()中,t.start()触发的就是它的run()方法:

private final class Worker
        extends AbstractQueuedSynchronizer
        implements Runnable {
    /**
     * This class will never be serialized, but we provide a
     * serialVersionUID to suppress a javac warning.
     */
    private static final long serialVersionUID = 6138294804551838833L;

    /**
     * Thread this worker is running in.  Null if factory fails.
     */
    final Thread thread;
    /**
     * Initial task to run.  Possibly null.
     */
    Runnable firstTask;
    /**
     * Per-thread task counter
     */
    volatile long completedTasks;

    /**
     * Creates with given first task and thread from ThreadFactory.
     *
     * @param firstTask the first task (null if none)
     */
    Worker(Runnable firstTask) {
        setState(-1); // inhibit interrupts until runWorker
        this.firstTask = firstTask;
        // 这边是把Runnable传给了Thread,也就是说Thread.run()就是执行了下面的run()方法
        this.thread = getThreadFactory().newThread(this);
    }

    /**
     * Delegates main run loop to outer runWorker
     */
    public void run() {
        runWorker(this);
    }
}

run()方法实际调用了runWorker(Worker)方法

runWorker(Worker)方法:

final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        w.unlock(); // 释放锁,允许中断
        boolean completedAbruptly = true;
        try {
            // 1. worker中的task不为空
            // 2. 如果worker的task为空,那么取WorkerQueue的task
            while (task != null || (task = getTask()) != null) {
                w.lock();
                // If pool is stopping, ensure thread is interrupted;
                // if not, ensure thread is not interrupted.  This
                // requires a recheck in second case to deal with
                // shutdownNow race while clearing interrupt
                if ((runStateAtLeast(ctl.get(), STOP) ||
                        (Thread.interrupted() &&
                                runStateAtLeast(ctl.get(), STOP))) &&
                        !wt.isInterrupted())
                    wt.interrupt();
                try {
                    // 这是一个空方法,可由子类实现
                    beforeExecute(wt, task);
                    Throwable thrown = null;
                    try {
                        // 执行task
                        task.run();
                    }
                    .... 省略
                    // 这是一个空方法,可由子类实现
                    finally {
                        afterExecute(task, thrown);
                    }
                } finally {
                    task = null;
                    w.completedTasks++;
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
            processWorkerExit(w, completedAbruptly);
        }
    }

getTask():

```java
private Runnable getTask() {
    // 进入死循环
    for (; ; ) {
        try {
            // 为true的条件:
            // allowCoreThreadTimeOut=true: 核心线程需根据keepAliveTime超时等待
            // 核心线程数已满
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
            // 如果timed为true,执行BlockQueue.poll(),这个操作在取不到task的时候会等待keepAliveTime,然后返回null
            // 如果timed为false,执行BlockQueue.take(),这个操作在队列为空的时候一直阻塞
            Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
            if (r != null)
                return r;
        }
    }
}
```

线程池的源码按照上述的几个方法(execute(runnable) -> addWorker(runnable,core) -> Worker -> runWorker(worker) -> getTask())的顺序来分析,你就可以很清晰的将运作过程了解清楚,同事构造方法和几个重要的参数一定要懂,不然对于后面的源码分析很受阻碍,相信大家通过这篇文章可以加深对线程池的理解。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Android线程池控制并发数多线程下载

    多线程下载并不是并发下载线程越多越好,因为当用户开启太多的并发线程之后,应用程序需要维护每条线程的开销,线程同步的开销. 这些开销反而会导致下载速度降低.因此需要避免在代码中直接开启大量线程执行下载. 主要实现步奏: 1.定义一个DownUtil类,下载工作基本在此类完成,在构造器中初始化UI线程的Handler.用于子线程和UI线程传递下载进度值. 2.所有的下载任务都保存在LinkedList.在init()方法中开启一个后台线程,不断地从LinkedList中取任务交给线程池中的空闲线程执

  • 分析Android中线程和线程池

    目录 前言 HandlerThread IntentService 线程池的好处 ThreadPoolExecutor 线程池的分类 FixedThreadPool CachedThreadPool ScheduledThreadPool SingleThreadExecutor 前言 由于内容过多,所以将分为上下两部分,第一部分主要和大家谈谈Android中的线程,以及在Android中的常用的线程池.第二部分我们一起来了解一下AsyncTask的使用和工作原理. HandlerThread

  • 浅谈Android中线程池的管理

    说到线程就要说说线程机制 Handler,Looper,MessageQueue 可以说是三座大山了 Handler Handler 其实就是一个处理者,或者说一个发送者,它会把消息发送给消息队列,也就是Looper,然后在一个无限循环队列中进行取出消息的操作 mMyHandler.sendMessage(mMessage); 这句话就是我耗时操作处理完了,我发送过去了! 然后在接受的地方处理!简单理解是不是很简单. 一般我们在项目中异步操作都是怎么做的呢? // 这里开启一个子线程进行耗时操作

  • Android线程池源码阅读记录介绍

    今天面试被问到线程池如何复用线程的?当场就懵掉了...于是面试完毕就赶紧打开源码看了看,在此记录下: 我们都知道线程池的用法,一般就是先new一个ThreadPoolExecutor对象,再调用execute(Runnable runnable)传入我们的Runnable,剩下的交给线程池处理就行了,于是这次我就从ThreadPoolExecutor的execute方法看起: public void execute(Runnable command) { if (command == null)

  • Android开发经验谈:并发编程(线程与线程池)(推荐)

    一.线程 在Android开发中,你不可能都在主线程中开发,毕竟要联网,下载数据,保存数据等操作,当然这就离不开线程. (当然你可以在Android4.0以前的手机里在主线程请求网络,我最早开发的时候,用的手机比较古老...) 在Android中你可以随意创建线程,于是就会造成线程不可控,内存泄漏,创建线程消耗资源,线程太多了消耗资源等问题. 具体线程怎么创建我就不在文章里描述了,毕竟这主要将并发编程.... 大家知道线程不可控就好了...于是就需要对线程进行控制,防止一系列问题出现,这就用到了

  • Android开发中线程池源码解析

    线程池(英语:thread pool):一种线程使用模式.线程过多会带来调度开销,进而影响缓存局部性和整体性能.而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务.这避免了在处理短时间任务时创建与销毁线程的代价.线程池不仅能够保证内核的充分利用,还能防止过分调度.可用线程数量应该取决于可用的并发处理器.处理器内核.内存.网络sockets等的数量. 例如,线程数一般取cpu数量+2比较合适,线程数过多会导致额外的线程切换开销.----摘自维基百科 我们在Android或者Java开发中

  • Android音视频开发Media FrameWork框架源码解析

    目录 一.Media FrameWork背景 二.Media Framework“路线图” 2.1 代理端 2.2 服务端 2.2.1 Source 2.2.2 Decoder 2.2.3 Renderer 2.2.4 Foundation 2.3 OMX端 2.4 Kernel端 三.media播放的流程 四.Media FrameWork源码分析 一.Media FrameWork背景 Media Framework (媒体函数库):此函数库让Android 可以播放与录制许多常见的音频与视

  • Android Handler,Message,MessageQueue,Loper源码解析详解

    本文主要是对Handler和消息循环的实现原理进行源码分析,如果不熟悉Handler可以参见博文< Android中Handler的使用>,里面对Android为何以引入Handler机制以及如何使用Handler做了讲解. 概括来说,Handler是Android中引入的一种让开发者参与处理线程中消息循环的机制.我们在使用Handler的时候与Message打交道最多,Message是Hanlder机制向开发人员暴露出来的相关类,可以通过Message类完成大部分操作Handler的功能.但

  • Android开发数据结构算法ArrayList源码详解

    目录 简介 ArrayList源码讲解 初始化 扩容 增加元素 一个元素 一堆元素 删除元素 一个元素 一堆元素 修改元素 查询元素 总结 ArrayList优点 ArrayList的缺点 简介 ArrayList是List接口的一个实现类,它是一个集合容器,我们通常会通过指定泛型来存储同一类数据,ArrayList默认容器大小为10,自身可以自动扩容,当容量不足时,扩大为原来的1.5倍,和上篇文章的Vector的最大区别应该就是线程安全了,ArrayList不能保证线程安全,但我们也可以通过其

  • nginx内存池源码解析

    目录 内存池概述 一.nginx数据结构 二.nginx向OS申请空间ngx_create_pool 三.nginx向内存池申请空间 四.大块内存的分配与释放 五.关于小块内存不释放 六.销毁和清空内存池 七.编译测试内存池接口功能 内存池概述 内存池是在真正使用内存之前,预先申请分配一定数量的.大小相等(一般情况下)的内存块留作备用.当有新的内存需求时,就从内存池中分出一部分内存块,若内存块不够用时,再继续申请新的内存. 内存池的好处有减少向系统申请和释放内存的时间开销,解决内存频繁分配产生的

  • Netty组件NioEventLoopGroup创建线程执行器源码解析

    目录 通过上一章的学习, 我们了解了Server启动的大致流程, 有很多组件与模块并没有细讲, 从这个章开始, 我们开始详细剖析netty的各个组件, 并结合启动流程, 将这些组件的使用场景及流程进行一个详细的说明 这一章主要学习NioEventLoop相关的知识, 何为NioEventLoop? NioEventLoop是netty的一个线程, 在上一节我们创建两个NioEventLoopGroup: EventLoopGroup bossGroup = new NioEventLoopGro

  • Android 开发中线程的分析

    Android 开发中线程的分析 今天早上把公司给的任务做完了之后,突然就有点无聊,于是,把以前学的那些东西翻了翻,博客看了看,就看到一个关于线程的博客,有了很大的争议,我也差点误解了(感觉高大上~~~).整体代码差不多是这样: package sw.angel.thread; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.util.Log; pub

  • Vue中$nextTick实现源码解析

    目录 正文 先看一个简单的问题 内部实现 先看第一块: 再看第二块: 然后是第三块: 最后是第四块: 正文 先看一个简单的问题 <template> <div @click="handleClick" ref="div">{{ text }}</div </template> <script> export default { data() { return { text: 'old' } }, methods:

  • 完全解析Android多线程中线程池ThreadPool的原理和使用

    前言对于多线程,大家应该很熟悉.但是,大家了解线程池吗?今天,我将带大家全部学习关于线程池的所有知识. 目录 1. 简介 2. 工作原理 2.1 核心参数线程池中有6个核心参数,具体如下 上述6个参数的配置 决定了 线程池的功能,具体设置时机 = 创建 线程池类对象时 传入 ThreadPoolExecutor类 = 线程池的真正实现类 开发者可根据不同需求 配置核心参数,从而实现自定义线程池 // 创建线程池对象如下 // 通过 构造方法 配置核心参数 Executor executor =

随机推荐