Java线程池源码简析

上一篇介绍了线程池中的几种类型,本文来简单分析一下线程池ThreadPoolExecutor的源码。

首先来看实例域

ctl:代表线程池的控制状态,使用这个变量标识线程池的状态,它的值通过runState与workCount同时确定。workCount为允许开启并且不允许停止的线程数量,可以把它看做线程池中存活的线程数量。runState提供主要的生命周期控制,他有以下几种状态,RUNNING:运行中,可以接收新任务并处理排队任务;SHUTDOWN:停止接收新任务,但是可以处理排队中的任务;STOP:不接受新任务,不处理排队任务,中断正在进行中的任务,TIDYING:所有任务被终止,workCount为0,转换到该状态的线程将执行terminated()方法;TERMINATED:意味着terminated()方法执行完成。runState将会随着运行时间的增长而改变,但不需要到达每个状态。线程池的doc中给出了几种可能的状态转换。RUNNING -> SHUTDOWN:调用了shutdown方法,或者finalize方法;(RUNNING or SHUTDOWN) -> STOP:调用了shutdownNow方法;SHUTDOWN -> TIDYING:队列与线程池均为空时;STOP -> TIDYING:线程池为空时;TIDYING -> TERMINATED:terminated方法执行完成时。

execute与submit

我们使用线程池执行任务时是调用线程池的execute或者submit方法,后者可以获取任务返回值,这俩个方法是线程池的执行入口,我们着重看一下execute方法,submit方法返回future对象基本也是相同的逻辑,就不贴代码了。

execute方法内部

这里的commond是一个Runnable对象。首先使用workerCountOf(c)计算出线程数量是否小于核心线程数,若小于则调用addWorker尝试添加任务,创建核心线程。再使用isRunning(c) &&workQueue.offer(command)判断线程池运行状态以及队列能否新加任务,这里的状态就是线程池中线程数已达到核心线程数但还未达到最大线程数。接下来!isRunning(recheck) && remove(command)再次检查线程池运行状态(上次校验已过期,防止并发),若线程池状态为非running,则移除已经添加到队列中的任务并执行拒绝策略。else if判断workerCountOf(recheck) ==0线程池中工作线程是否为0,可能大家会有疑问,核心线程不是不能被回收的吗,线程数怎么会为0呢,其实线程池提供了allowCoreThreadTimeOut参数,当该值为true时(默认为false),当核心线程空闲时间超过keepLiveTime时,核心线程也将被回收,这时虽然池中没有可用线程,但是任务同样需要执行,也会调用addWorker方法。这里的状态就是核心线程已满,将任务放入阻塞队列。!addWorker(command,false)判断是否阻塞队列已满并且是否超过了最大线程数,满+超则执行拒绝策略。可以看到,他主要通过addWorker的俩个参数来区分各种情况,我们继续来看addWorker方法。

addworker

第一个参数firstTask表示新线程应该首先运行的任务(如果没有,则为null)。第二个参数core代表是否使用核心线程。

首先判断线程池状态是否为非运行状态,若是直接拒绝返回false。第二个判断根据core的值判断线程数是否大于核心或最大线程数,若是直接返回false。

进入代码下半部分则可以正常的添加任务了。首先将任务封装成worker,这是线程池内部对于Runnable的封装

可以看到他只是对runnable和thread进行了简单封装,主要方法run也只是调用了runWorker方法(后面会说)。继续addWorker方法的分析,封装为worker后,进行加锁控制将任务加入到hashset中并启动worker中线程,由worker源码可以发现,线程工厂在创建线程thread时,将Woker作为参数传入,当执行start方法启动线程thread时,因为worker实现runnable方法,所以其实是执行了Worker的runWorker方法。执行后最终返回线程启动结果。接下来继续分析runWorker方法

runWorker

task不为空时直接运行该task,为空时直接调用getTask从阻塞队列中获取。这里有个扩展点就是任务执行运行前后会有beforeExecute和afterExecute的回调,用于执行自定义代码。

结尾

到这里线程池大体的执行逻辑就大致介绍完了,我个人觉得看源码的时候不要深陷于细节中,因为库函数要考虑的东西太多,而且大神的代码也不是我等凡人能轻松理解的。。清楚很重要,具体的细节有精力的话可以深入研究。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342

推荐阅读更多精彩内容

  • 梅子是那个秋天认识青木的。 那是深秋的一个午后,梅子去【一村画室】找她的闺蜜竹子。不料竹子出去买颜料了。一个清瘦而...
    湍河故事阅读 694评论 0 4
  • 你沉默不语 情绪一层层在脸上晕染开来 路灯依次在黑夜中亮起 仿佛是你的语言 你把天空和我折叠起来 放进口袋 不喜欢...
    mozik阅读 136评论 0 1
  • 《超级合作者》备忘录 当你跟人全情投入合作时,你就接纳了他。 我们是否可以做到跟一个人合作共事,但你可能完全不接纳...
    团的花园阅读 452评论 0 0
  • 欧盟国家智慧城市建设覆盖社会、环境和经济3个主要领域,其中智慧社会建设以人为本;智慧环境建设以节能减排为重点,保障...
    moreartedu阅读 456评论 0 0