【Android 异步操作】线程池 ( Worker 简介 | 线程池中的工作流程 runWorker | 从线程池任务队列中获取任务 getTask )

简介: 【Android 异步操作】线程池 ( Worker 简介 | 线程池中的工作流程 runWorker | 从线程池任务队列中获取任务 getTask )

文章目录

一、线程池中的 Worker ( 工作者 )

二、线程池中的工作流程 runWorker

三、线程池任务队列中获取任务 getTask



在博客 【Android 异步操作】线程池 ( 线程池 execute 方法源码解析 ) 中 , 讲解 线程池 ThreadPoolExecutor 的 execute 方法时 , 有两个重要的核心方法 ;


两个核心的操作 :


添加任务 : addWorker(command, true) , 第二个参数为 true 是添加核心线程任务 , 第二个参数为 false 是添加非核心线程任务 ;

拒绝任务 : reject(command)


在上一篇博客 【Android 异步操作】线程池 ( 线程池 reject 拒绝任务 | 线程池 addWorker 添加任务 ) 介绍了 addWorker 添加任务 , reject 拒绝任务 的源码细节 ;



本博客中介绍 Worker ( 工作者 ) 的相关源码






一、线程池中的 Worker ( 工作者 )


工作者 Worker 主要 为线程执行任务 , 维护终端控制状态 , 同时记录其它信息 ;


该类扩展了 AbstractQueuedSynchronizer , 目的是 简化 每个任务执行时 获取和释放锁的过程 ;


该操作可以防止中断用于唤醒等待任务的工作线程 , 不会中断一个正在运行的线程 ;



Worker 代码及相关注释说明 :


public class ThreadPoolExecutor extends AbstractExecutorService {
    /**
     * 工作者类主要为线程执行任务 , 维护终端控制状态 , 同时记录其它信息 ; 
     * 该类扩展了 AbstractQueuedSynchronizer , 目的是简化 每个任务执行时 获取和释放锁的过程 ; 
     * 该操作可以防止中断用于唤醒等待任务的工作线程 , 不会中断一个正在运行的线程 ; 
     */
    private final class Worker
        extends AbstractQueuedSynchronizer
        implements Runnable
    {
        /**
         * 该类不会被序列化, 提供该常量用于支持 Java 文档警告 .
         */
        private static final long serialVersionUID = 6138294804551838833L;
        /** 该工作者运行的线程 , 如果线程工厂创建失败 , 就为空. */
        final Thread thread;
        /** 线程初始任务 , 可能为空. */
        Runnable firstTask;
        /** 每个线程的任务计数 */
        volatile long completedTasks;
        /**
         * 使用线程工厂 , 根据给定的初始任务 , 创建工作者
         */
        Worker(Runnable firstTask) {
            setState(-1); // inhibit interrupts until runWorker
            this.firstTask = firstTask;
    // 线程是在构造函数中 , 使用线程工厂创建的 
            this.thread = getThreadFactory().newThread(this);
        }
        /** 将主要的循环操作委托给了外部的 runWorker , 本博客下面有该方法的解析 . */
        public void run() {
            runWorker(this);
        }
        // 锁相关方法 
        //
        // 0 代表未锁定状态 .
        // 1 代表锁定状态 .
        protected boolean isHeldExclusively() {
            return getState() != 0;
        }
        protected boolean tryAcquire(int unused) {
            if (compareAndSetState(0, 1)) {
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }
        protected boolean tryRelease(int unused) {
            setExclusiveOwnerThread(null);
            setState(0);
            return true;
        }
        public void lock()        { acquire(1); }
        public boolean tryLock()  { return tryAcquire(1); }
        public void unlock()      { release(1); }
        public boolean isLocked() { return isHeldExclusively(); }
        void interruptIfStarted() {
            Thread t;
            if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
                try {
                    t.interrupt();
                } catch (SecurityException ignore) {
                }
            }
        }
    }
}




二、线程池中的工作流程 runWorker


 

/**
     * Main worker run loop.  Repeatedly gets tasks from queue and
     * executes them, while coping with a number of issues:
     *
     * 1. We may start out with an initial task, in which case we
     * don't need to get the first one. Otherwise, as long as pool is
     * running, we get tasks from getTask. If it returns null then the
     * worker exits due to changed pool state or configuration
     * parameters.  Other exits result from exception throws in
     * external code, in which case completedAbruptly holds, which
     * usually leads processWorkerExit to replace this thread.
     *
     * 2. Before running any task, the lock is acquired to prevent
     * other pool interrupts while the task is executing, and then we
     * ensure that unless pool is stopping, this thread does not have
     * its interrupt set.
     *
     * 3. Each task run is preceded by a call to beforeExecute, which
     * might throw an exception, in which case we cause thread to die
     * (breaking loop with completedAbruptly true) without processing
     * the task.
     *
     * 4. Assuming beforeExecute completes normally, we run the task,
     * gathering any of its thrown exceptions to send to afterExecute.
     * We separately handle RuntimeException, Error (both of which the
     * specs guarantee that we trap) and arbitrary Throwables.
     * Because we cannot rethrow Throwables within Runnable.run, we
     * wrap them within Errors on the way out (to the thread's
     * UncaughtExceptionHandler).  Any thrown exception also
     * conservatively causes thread to die.
     *
     * 5. After task.run completes, we call afterExecute, which may
     * also throw an exception, which will also cause thread to
     * die. According to JLS Sec 14.20, this exception is the one that
     * will be in effect even if task.run throws.
     *
     * The net effect of the exception mechanics is that afterExecute
     * and the thread's UncaughtExceptionHandler have as accurate
     * information as we can provide about any problems encountered by
     * user code.
     *
     * @param w the worker
     */
    final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        // 拿到了一个任务 
        Runnable task = w.firstTask;
        w.firstTask = null;
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
    // 第一次循环 task 不为空 , 直接就进入了循环体 
    // 第二次循环 task 为空 , 此时执行 || 后的逻辑 (task = getTask()) != null
    //    该逻辑中从线程池任务队列中获取任务 , 然后执行该任务 
    // 此处一直循环读取线程池任务队列中的任务并执行 
            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.run();
                    } catch (RuntimeException x) {
                        thrown = x; throw x;
                    } catch (Error x) {
                        thrown = x; throw x;
                    } catch (Throwable x) {
                        thrown = x; throw new Error(x);
                    } finally {
                        afterExecute(task, thrown);
                    }
                } finally {
                    task = null;
                    w.completedTasks++;
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
            processWorkerExit(w, completedAbruptly);
        }
    }






三、线程池任务队列中获取任务 getTask


getTask 从 线程池 任务队列中 获取任务 , 该方法执行 阻塞 或 定时等待 任务 , 具体执行哪个需要根据当前的配置情况 ;


这里通过 线程数 判断该线程是 核心线程 , 还是 非核心线程 ;



非核心线程 :


判定条件 : 如果当前执行的线程 大于 核心线程数 , 就是非核心线程

获取方法 : 非核心线程 调用 poll 方法从任务队列中取任务

线程回收 : 如果超过 keepAliveTime 时间还取不到任务 , 非核心线程 空闲时间 超过了一定时间 , 此时需要回收


核心线程 :


获取方法 : 如果该线程是核心线程 , 那么就会调用 take 方法 , 而不是 poll 方法

阻塞方法 : take 方法是阻塞的

不会被回收 : 核心线程不会回收 , 非核心线程超过一定时间会被回收


如果出现下面 4 中情况 , 工作者必须退出 , 该方法返回 null :


工作者数量超过线程池个数

线程池停止

线程池关闭 , 任务队列清空

该工作者等待时间超过空闲时间 , 需要被回收 ; 前提是该线程是非和核心线程 ;


getTask 相关源码 :


 

/**
     * 执行阻塞或定时等待任务 , 具体执行哪个需要根据当前的配置情况 ; 
     * 
     * 如果出现下面 4 中情况 , 工作者必须退出 , 该方法返回 null : 
     * 1 . 工作者数量超过线程池个数 
     * 2 . 线程池停止
     * 3 . 线程池关闭 , 任务队列清空 
     * 4 . 该工作者等待时间超过空闲时间 , 需要被回收 ; 前提是该线程是非和核心线程 ; 
     *
     * @return 返回要执行的任务 ; 如果返回空 , 说明该 工作者 Worker 必须退出 , 工作者计数 -1
     */
    private Runnable getTask() {
        boolean timedOut = false; // Did the last poll() time out?
        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);
            // Check if queue empty only if necessary.
            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
                decrementWorkerCount();
                return null;
            }
            int wc = workerCountOf(c);
            // Are workers subject to culling?
            // 如果 wc 大于 核心线程数 , 说明本线程是非核心线程 
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
            if ((wc > maximumPoolSize || (timed && timedOut))
                && (wc > 1 || workQueue.isEmpty())) {
                if (compareAndDecrementWorkerCount(c))
                    return null;
                continue;
            }
            try {
              // 这里进行了时间判断 
              // 如果当前执行的线程 大于 核心线程数 , 就是非核心线程 
              // 调用 poll 方法从任务队列中取任务, 如果超过 keepAliveTime 时间还取不到任务 , 
              // 非核心线程 空闲时间 超过了一定时间 , 此时需要回收 
    // 如果该线程是核心线程 , 那么就会调用 take 方法 , 而不是 poll 方法
    // take 方法是阻塞的   
    // 因此核心线程不会回收 , 非核心线程超过一定时间会被回收 
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
                if (r != null)
                    return r;
                timedOut = true;
            } catch (InterruptedException retry) {
                timedOut = false;
            }
        }
    }



目录
打赏
0
0
0
0
39
分享
相关文章
|
15天前
|
【Java并发】【线程池】带你从0-1入门线程池
欢迎来到我的技术博客!我是一名热爱编程的开发者,梦想是编写高端CRUD应用。2025年我正在沉淀中,博客更新速度加快,期待与你一起成长。 线程池是一种复用线程资源的机制,通过预先创建一定数量的线程并管理其生命周期,避免频繁创建/销毁线程带来的性能开销。它解决了线程创建成本高、资源耗尽风险、响应速度慢和任务执行缺乏管理等问题。
139 60
【Java并发】【线程池】带你从0-1入门线程池
阿里面试:5000qps访问一个500ms的接口,如何设计线程池的核心线程数、最大线程数? 需要多少台机器?
本文由40岁老架构师尼恩撰写,针对一线互联网企业的高频面试题“如何确定系统的最佳线程数”进行系统化梳理。文章详细介绍了线程池设计的三个核心步骤:理论预估、压测验证和监控调整,并结合实际案例(5000qps、500ms响应时间、4核8G机器)给出具体参数设置建议。此外,还提供了《尼恩Java面试宝典PDF》等资源,帮助读者提升技术能力,顺利通过大厂面试。关注【技术自由圈】公众号,回复“领电子书”获取更多学习资料。
Unity多线程使用(线程池)
在C#中使用线程池需引用`System.Threading`。创建单个线程时,务必在Unity程序停止前关闭线程(如使用`Thread.Abort()`),否则可能导致崩溃。示例代码展示了如何创建和管理线程,确保在线程中执行任务并在主线程中处理结果。完整代码包括线程池队列、主线程检查及线程安全的操作队列管理,确保多线程操作的稳定性和安全性。
安卓与iOS开发中的线程管理差异解析
在移动应用开发的广阔天地中,安卓和iOS两大平台各自拥有独特的魅力。如同东西方文化的差异,它们在处理多线程任务时也展现出不同的哲学。本文将带你穿梭于这两个平台之间,比较它们在线程管理上的核心理念、实现方式及性能考量,助你成为跨平台的编程高手。
深入探索Android与iOS的多线程编程差异
在移动应用开发领域,多线程编程是提高应用性能和响应性的关键。本文将对比分析Android和iOS两大平台在多线程处理上的不同实现机制,探讨它们各自的优势与局限性,并通过实例展示如何在这两个平台上进行有效的多线程编程。通过深入了解这些差异,开发者可以更好地选择适合自己项目需求的技术和策略,从而优化应用的性能和用户体验。
深入探索Android与iOS的多任务处理机制
在移动操作系统领域,Android和iOS各有千秋,尤其在多任务处理上展现出不同的设计理念和技术实现。本文将深入剖析两大平台在后台管理、资源分配及用户体验方面的策略差异,揭示它们如何平衡性能与电池寿命,为用户带来流畅而高效的操作体验。通过对比分析,我们不仅能够更好地理解各自系统的工作机制,还能为开发者优化应用提供参考。
|
4月前
|
.如何根据 CPU 核心数设计线程池线程数量
IO 密集型:核心数*2 计算密集型: 核心数+1 为什么加 1?即使当计算密集型的线程偶尔由于缺失故障或者其他原因而暂停时,这个额外的线程也能确保 CPU 的时钟周期不会被浪费。
210 4
|
1月前
|
Linux编程: 在业务线程中注册和处理Linux信号
本文详细介绍了如何在Linux中通过在业务线程中注册和处理信号。我们讨论了信号的基本概念,并通过完整的代码示例展示了在业务线程中注册和处理信号的方法。通过正确地使用信号处理机制,可以提高程序的健壮性和响应能力。希望本文能帮助您更好地理解和应用Linux信号处理,提高开发效率和代码质量。
49 17
|
1月前
|
Linux编程: 在业务线程中注册和处理Linux信号
通过本文,您可以了解如何在业务线程中注册和处理Linux信号。正确处理信号可以提高程序的健壮性和稳定性。希望这些内容能帮助您更好地理解和应用Linux信号处理机制。
59 26
|
3月前
|
Java多线程编程秘籍:各种方案一网打尽,不要错过!
Java 中实现多线程的方式主要有四种:继承 Thread 类、实现 Runnable 接口、实现 Callable 接口和使用线程池。每种方式各有优缺点,适用于不同的场景。继承 Thread 类最简单,实现 Runnable 接口更灵活,Callable 接口支持返回结果,线程池则便于管理和复用线程。实际应用中可根据需求选择合适的方式。此外,还介绍了多线程相关的常见面试问题及答案,涵盖线程概念、线程安全、线程池等知识点。
286 2

热门文章

最新文章

  • 1
    【03】微信支付商户申请下户到配置完整流程-微信开放平台创建APP应用-填写上传基础资料-生成安卓证书-获取Apk签名-申请+配置完整流程-优雅草卓伊凡
    64
  • 2
    android FragmentManager 删除所有Fragment 重建
    25
  • 3
    Android实战经验之Kotlin中快速实现MVI架构
    41
  • 4
    即时通讯安全篇(一):正确地理解和使用Android端加密算法
    41
  • 5
    escrcpy:【技术党必看】Android开发,Escrcpy 让你无线投屏新体验!图形界面掌控 Android,30-120fps 超流畅!🔥
    45
  • 6
    【01】噩梦终结flutter配安卓android鸿蒙harmonyOS 以及next调试环境配鸿蒙和ios真机调试环境-flutter项目安卓环境配置-gradle-agp-ndkVersion模拟器运行真机测试环境-本地环境搭建-如何快速搭建android本地运行环境-优雅草卓伊凡-很多人在这步就被难倒了
    155
  • 7
    Cellebrite UFED 4PC 7.71 (Windows) - Android 和 iOS 移动设备取证软件
    53
  • 8
    【03】仿站技术之python技术,看完学会再也不用去购买收费工具了-修改整体页面做好安卓下载发给客户-并且开始提交网站公安备案-作为APP下载落地页文娱产品一定要备案-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
    73
  • 9
    Android历史版本与APK文件结构
    179
  • 10
    【02】仿站技术之python技术,看完学会再也不用去购买收费工具了-本次找了小影-感觉页面很好看-本次是爬取vue需要用到Puppeteer库用node.js扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
    54
  • AI助理

    你好,我是AI助理

    可以解答问题、推荐解决方案等