ThreadPoolExecutor源码解析(二)

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
简介: 1.ThreadPoolExcuter运行实例   首先我们先看如何新建一个ThreadPoolExecutor去运行线程。然后深入到源码中去看ThreadPoolExecutor里面使如何运作的。 public class Test { public static void main(S.

1.ThreadPoolExcuter运行实例

  首先我们先看如何新建一个ThreadPoolExecutor去运行线程。然后深入到源码中去看ThreadPoolExecutor里面使如何运作的。

public class Test {
    public static void main(String[] args){
        /**
         * 新建一个线程池
         * corePoolSize:2
         * maximumPoolSize:10
         * keepAliveTime:20
         * unit:TimeUnit.SECONDS(秒)
         * workQueue:new ArrayBlockingQueue(10)
         * threadFactory:默认
         * RejectedExecutionHandler默认
         */
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2,10,20, TimeUnit.SECONDS,new ArrayBlockingQueue(10));
        //用execute添加一个线程
        threadPool.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
    }
}

2.ThreadPoolExecute.execute方法

  可以发现,其实使用线程池就是使用这个方法,然后我们看这个方法具体的代码。

    /**
     * 在后面执行给定任务。任务在一个新的线程中或一个存在的worker的线程池中执行。
     * 如果一个线程不能提交到excution,可能是因为这个excutor已经shundown或者因为其容量已经是最大,
     * 此时任务将会被RejectedExecutionHandler处理
     *
     */
    public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        /*
         * Proceed in 3 steps:
         * 有以下3个步骤
         *
         * 1.如果少于corePoolSize的线程在运行,那么试着启动一个新线程,其中用给定指令作为first task。
         * 这会调用addWorker去原子性得检查runState和workerCoune,因此可以防止错误报警,在错误报警不应该时通过返回false来添加线程
         * 2.如果任务被成功排队,我们任然应该第二次检查是否添加一个新线程(因为可能存在在最后一次检查后挂掉的情况)
         * 或者在进入这个方法期间线程池shutdown。所以我们再次检查状态,如果已关闭和有必要则退出队列,或者如果没有的话就开始一个新的线程。
         * 3.如果我们无法将task入队,那么我们试图添加新线程。如果失败,那么知道我们shutdown或者是饱和的并拒绝task。
         */
        int c = ctl.get();
        //判断是否小于corePoolSize
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        //如果pool在运行并且能提交到队列
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            //这里进行再次检查,如果线程池没在运行并且成功删除task后,使用拒绝策略拒绝该task
            if (! isRunning(recheck) && remove(command))
                reject(command);
//如果已经将task添加到队列中,而此时没有worker的话,那么新建一个worker。稍后这个空闲的worker就会自动去队列里面取任务来执行
else if (workerCountOf(recheck) == 0) addWorker(null, false); } //如果无法提交那么按照拒绝策略拒绝task else if (!addWorker(command, false)) reject(command); }

  线程池的基础和Worker基本介绍在前一节已经有说过,可以点这里查看。可以看到这个方法的主要流程,其实都在注释里面说明了。可以发现里面主要调用了一个方法,addWorker()。 那么这个addWorker()又是什么东西呢。其实看方法名就很清楚了,就是新建一个Worker来执行你添加进来的task。

3.ThreadPoolExecute.addWorker()方法

    /**
     * 检查当前的线程池状态和容量,是否可以让一个新的worker加入。如果可以,worker计数将会被调整,并且
     * 如果可能,一个新的woker将会被创建和开始,将它当作第一个任务来运行。当线程池是stopped或shutdown状态时,
     * 将返回false。当线程工厂创建失败而返回null或者抛出exception(比如典型的OOM)时,它也会返回fails。
     * firstTask:新线程应该第一个运行的任务。当线程数少于corePoolSize时或是队列满时,workers使用一个初始化的
     * first task来创建,用来进行分流。初始化空闲线程通常使用prestartCoreThread。
     * core:为true,如果使用有界的corePoolSize,否则时maxPoolSize
     * @return true if successful
     * 添加Worker
     */
    private boolean addWorker(Runnable firstTask, boolean core) {
        retry:
        for (;;) {
            int c = ctl.get();
            //状态值
            int rs = runStateOf(c);

            // Check if queue empty only if necessary.
            //关于状态值的检测
            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;
                //用到了原子CAS方法比较,使用CAS增加worker计数器成功,才能进入下一步
                if (compareAndIncrementWorkerCount(c))
                    break retry;
                //重新获取ctl
                c = ctl.get();  // Re-read ctl
                //这里表示执行到这里的时候线程池的运行状态改变,需要重新跳到retry处执行
                if (runStateOf(c) != rs)
                    continue retry;
                // else CAS failed due to workerCount change; retry inner loop
            }
        }

        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
            //使用firstTask初始化Worker,first可能为null,那么则表示该worker为空闲
            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());

                    if (rs < SHUTDOWN ||
                            (rs == SHUTDOWN && firstTask == null)) {
                        if (t.isAlive()) // precheck that t is startable
                            throw new IllegalThreadStateException();
                        workers.add(w);
                        int s = workers.size();
                        //largestPoolSize为跟踪的目前最大线程数,因为之前已经做过判断,所以不会越界问题
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                        workerAdded = true;
                    }
                } finally {
                    mainLock.unlock();
                }
                //workerAdded是在上面最后才设置的,确保这个变量能准确表示是否添加worker成功
                if (workerAdded) {
                    t.start();
                    workerStarted = true;
                }
            }
        } finally {
            //再次检查
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;
    }

  addWorker本事只是为线程池添加一个Worker,其本身所做的事情其实很简单,但难就难在要确保安全有效得添加一个Worker。为此addWorker()方法做了很多额外的工作。比如判断线程池的运行状态,当前Worker数量是否已经饱和等等。可以发现在这个方法,或者说整个ThreadPoolExecutor中,很多时候都是使用双重检查的方式来对线程池状态进行检查。其实这都是为了效率,最简单不过直接使用Synchronized或ReentranLock进行同步,但这样效率会低很多,所以在这里,只有在万不得已的情况下,才会使用悲观的ReentranLock。

  addWorker的最后直接调用了t.start,这里的t其实就是Worker它自己。接下来再看Worker是如何运行的。

4.ThreadPoolExecute.runWorker()方法

    /**
     * 主要的Worker运行的循环。重复得获取从任务队列中取出task并执行它。
    */

    final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        //取出firstTask,再将worker中的值-设置为null
        Runnable task = w.firstTask;
        w.firstTask = null;
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
            //不断循环取出线程运行
            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
                //如果当前线程是stop,那么将确认其为interrupted
                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);
        }
    }

  从源码中可以看出,一个Worker的工作其实就是不断使用getTask()方法从队列中获取新的任务来执行。值得一提的是,初始化参数里面的时间戳参数就是在这个方法里面运用的。在循环体中每次都使用锁以保证当前worker在运行task过程中不会被中断。同时运行时还会去调用两个内置的钩子:beforeExecute()和afterExecute(),这两个方法默认实现时空的。

  同时在运行的循环中每次都关注着ThreadPoolExecutor的运行状态,当线程池处于中断状态时,循环Worker的当前线程也会中断。

 

总结:说到这里就差不多把线程池运行task的流程说完了,当然其中忽略了很多的细节。但总而言之,ThreadPoolExecutor其实就是对worker进行管理,然后使用这些worker来执行用户提交的task。对用户提交的task的数量也进行一定的控制管理,比如超过一定数量时放入一个任务队列中等等。然后对线程池规定一些状态量,根据这些状态量对线程池进行控制。

 

 


 


相关文章
|
1月前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
76 2
|
2天前
|
存储 设计模式 算法
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
行为型模式用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,它涉及算法与对象间职责的分配。行为型模式分为类行为模式和对象行为模式,前者采用继承机制来在类间分派行为,后者采用组合或聚合在对象间分配行为。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象行为模式比类行为模式具有更大的灵活性。 行为型模式分为: • 模板方法模式 • 策略模式 • 命令模式 • 职责链模式 • 状态模式 • 观察者模式 • 中介者模式 • 迭代器模式 • 访问者模式 • 备忘录模式 • 解释器模式
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
|
2天前
|
设计模式 存储 安全
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
结构型模式描述如何将类或对象按某种布局组成更大的结构。它分为类结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者釆用组合或聚合来组合对象。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象结构型模式比类结构型模式具有更大的灵活性。 结构型模式分为以下 7 种: • 代理模式 • 适配器模式 • 装饰者模式 • 桥接模式 • 外观模式 • 组合模式 • 享元模式
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
|
2天前
|
设计模式 存储 安全
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
创建型模式的主要关注点是“怎样创建对象?”,它的主要特点是"将对象的创建与使用分离”。这样可以降低系统的耦合度,使用者不需要关注对象的创建细节。创建型模式分为5种:单例模式、工厂方法模式抽象工厂式、原型模式、建造者模式。
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
|
25天前
|
缓存 监控 Java
Java线程池提交任务流程底层源码与源码解析
【11月更文挑战第30天】嘿,各位技术爱好者们,今天咱们来聊聊Java线程池提交任务的底层源码与源码解析。作为一个资深的Java开发者,我相信你一定对线程池并不陌生。线程池作为并发编程中的一大利器,其重要性不言而喻。今天,我将以对话的方式,带你一步步深入线程池的奥秘,从概述到功能点,再到背景和业务点,最后到底层原理和示例,让你对线程池有一个全新的认识。
53 12
|
21天前
|
PyTorch Shell API
Ascend Extension for PyTorch的源码解析
本文介绍了Ascend对PyTorch代码的适配过程,包括源码下载、编译步骤及常见问题,详细解析了torch-npu编译后的文件结构和三种实现昇腾NPU算子调用的方式:通过torch的register方式、定义算子方式和API重定向映射方式。这对于开发者理解和使用Ascend平台上的PyTorch具有重要指导意义。
|
3天前
|
安全 搜索推荐 数据挖掘
陪玩系统源码开发流程解析,成品陪玩系统源码的优点
我们自主开发的多客陪玩系统源码,整合了市面上主流陪玩APP功能,支持二次开发。该系统适用于线上游戏陪玩、语音视频聊天、心理咨询等场景,提供用户注册管理、陪玩者资料库、预约匹配、实时通讯、支付结算、安全隐私保护、客户服务及数据分析等功能,打造综合性社交平台。随着互联网技术发展,陪玩系统正成为游戏爱好者的新宠,改变游戏体验并带来新的商业模式。
|
2月前
|
缓存 Java 程序员
Map - LinkedHashSet&Map源码解析
Map - LinkedHashSet&Map源码解析
79 0
|
2月前
|
算法 Java 容器
Map - HashSet & HashMap 源码解析
Map - HashSet & HashMap 源码解析
65 0
|
2月前
|
存储 Java C++
Collection-PriorityQueue源码解析
Collection-PriorityQueue源码解析
69 0

推荐镜像

更多