源码采访局:揭秘Java线程池的核心机密
各行各业都有基本功,例如医生,需要知道人体各个器官、各个系统的作用,知道细胞的作用、细菌和真菌的区别、病毒是怎么形成的,还得知道各种药的作用,如何对症下药等。
在程序员世界
前言 各行各业都有基本功,例如医生,需要知道人体各个器官、各个系统的作用,知道细胞的作用、细菌和真菌的区别、病毒是怎么形成的,还得知道各种药的作用,如何对症下药等。 在程序员世界里,也有着体现程序员基本功的东西,例如数据结构、算法、操作系统、网络等,特别是当今互联网,飞速发展下,迅速搞产品快速上线的时代已经不是主流,而且当下行业不景气;大厂们招聘标准上对基本功也是越来越看重,基本功的修炼已经刻不容缓。 所以,我想通过一种访谈的形式来慢慢揭开程序员各种基本功的细节,分享给一起同行的你们!! 嘉宾介绍 今天我们很荣幸的邀请到了一位Java世界的老前辈,ThreadPoolExecutor,Java线程团队Leader,被Doug Lea创造,2004年9月30日18:00PM,随着J2SE1.5的发布后,被大众熟知;在Java世界的15年里,他兢兢业业坚守着自己的岗位,在提高系统多核资源利用方面有着丰富的经验。 采访 作为线程团队的Leader,您是否了解您的团队成员?(ps:什么是线程?) 其实我的团队成员都不是我招收进来的,他们都是由我的大BOSS(早期程序员开荒者)招进来;说到了解的话,这就得从我的大BOOS招来的操作系统老大说起; 操作系统指挥我们的程序干活主要靠以下几种算法: 大部分操作系统(如Windows、Linux)的任务调度是采用时间片轮转的抢占式调度方式,系统把所有就绪进程按先入先出的原则排成一个队列。新来的进程加到就绪队列末尾。每当执行进程调度时,进程调度程序总是选出就绪队列的队首进程,让它在CPU上运行一个时间片的时间。在一个时间片结束时,发生时钟中断,调度程序据此暂停当前进程的执行,将其送到就绪队列的末尾,并通过上下文切换执行当前的队首进程,进程可以未使用完一个时间片,就让出CPU 进程是什么? 进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元。进程由内存空间(代码、数据、进程空间、打开的文件)和一个或多个线程组成。 介绍完上述的操作系统任务调度方式和进程的概念,我们再来聊聊我的团队成员到底是什么样的存在? 其实有进程就能满足一个程序的正常执行了,那么为什么还需要我和我的团队呢?其实是因为随着计算机的快速发展,对CPU的要求越来越高,进程之间切换的开销较大,已经无法满足越来越复杂的程序的要求了。我的大BOSS为了能支撑公司的发展,就叫操作系统BOSS去解决这个问题,操作系统BOSS就不得不寻找一种新人才,于是线程就诞生了。线程是程序执行中一个单一的顺序控制流程,是程序执行流的最小单元,是处理器调度和分派的基本单位。 有了线程也不代表代替了进程的地位,操作系统BOSS也比较聪明,让进程来指挥线程,一个进程可以有一个或多个线程,各个线程之间共享程序的内存空间(也就是所在进程的内存空间)。 一个标准的线程由线程ID、当前指令指针(PC)、寄存器和堆栈组成 您日常的工作有哪些呢?(ps:线程池到底做了什么?) 简单的说,我的工作就是来管理线程的,为啥需要管理呢?一般来说,我的团队成员也就那么10几个(ps:CPU密集型一般是N+1,I/O密集型一般是2N+1,其中N是CPU总核数),公司招一个人的成本也是很高的(ps:线程创建时间)线程池linux,而线程们也不会自己干活,他们没事的时候就休息着,等到有任务过来时,我就找正在休息的人来干活。 能谈谈您具体是怎么管理您的团队成员么?(如何管理调度线程执行) 我的管理方式主要是依靠以下几个宝物: 状态控制器ctl:用于管理线程池状态和工作者线程个数线程池大小配置corePoolSize和maximumPoolSize:决定了最大能运行多少线程阻塞队列workQueue:当无空闲线程时,暂存在阻塞队列,等待poll出执行 状态控制器ctl 怎么去判断大家什么时候是休息状态,什么时候是工作状态呢;这就靠我的状态控制器ctl,它包含两部分: 线程池状态runState:通过这个状态我就能知道什么时候能分配任务,什么时候该下班休息工作者线程个数:workCount 这里先介绍两个常量和获取这两部分信息的方式: // 将32位int分割为3和29,前三位用于存储线程池状态,后面的位数表示工作者线程个数 int COUNT_BITS = Integer.SIZE - 3; // 工作者线程个数,最大为2^29-1 int CAPACITY = (1 << COUNT_BITS) - 1; /** 1. runState:高三位来代表线程池状态,runState 2. workCount:表示工作者个数 */ int runStateOf(int c) { return c & ~CAPACITY; } int workerCountOf(int c) { return c & CAPACITY; } int ctlOf(int rs, int wc) { return rs | wc; } 线程池状态主要有以下几种: 其中TERMINATED > TIDYING > STOP > SHUTDOWN > RUNNING 状态转换过程为: 如下图: 线程调度 整个线程调度以来上述几个法宝:线程池大小、阻塞队列和状态控制器,具体思路如下: 如下图: 下面让我们看看具体实现: addWorker:创建一个新的Worker用于执行Runnable Worker的run方法实际调用的是ThreadPoolExecutor#runWorker方法:如果工作者本身带有task则执行,否则会从阻塞队列workQueue中poll中一个task进行执行 能谈谈您工作这么多年下来的感受么? 15年,我从一个20出头的小伙子到如今快40岁的中年人,我也经历过高峰(ps:线程池运用合理)也经历过低谷(ps:线程池大小随意设置),也坐过冷板凳(ps:直接使用new Thread而不使用线程池)。总之,不忘初心、牢记使命,我不知道我还能为公司服务多久(ps:Java的未来会如何?),但我会站好每一班岗,为公司为程序员们管理好线程们。 如果你也想要保持”Stay Hungry Stay Foolish “和”每天进步一点点 “的精神,欢迎大家和我一起学习,每天都感受自己的成长。一起加油一起努力!! 如果觉得本文有帮助,请动动小手分享给更多人,一起学习!! (编辑:92站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |