博客
关于我
Java线程池源码及原理
阅读量:445 次
发布时间:2019-03-06

本文共 3226 字,大约阅读时间需要 10 分钟。

目录

1 说明

下面如果有贴出源码,对应的源码是JDK8

主要的源码类
java.util.concurrent.ThreadPoolExecutor、
java.util.concurrent.ThreadPoolExecutor.Worker
java.util.concurrent.AbstractExecutorService

1.1类继承图

2 线程池的状态

3 源码分析

3.1完整的线程池构造方法

public ThreadPoolExecutor(int corePoolSize,                          int maximumPoolSize,                          long keepAliveTime,                          TimeUnit unit,                          BlockingQueue
workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)

3.2 ctl

内部有重要的成员变量ctl,类型是AtomicInteger,低29位表示线程池中线程数,通过高3位表示线程池的运行状态

COUNT_BITS的值是29
1、RUNNING:-1 << COUNT_BITS,即高3位为111,该状态的线程池会接收新任务;
2、SHUTDOWN: 0 << COUNT_BITS,即高3位为000,该状态的线程池不会接收新任务;
3、STOP : 1 << COUNT_BITS,即高3位为001;
4、TIDYING : 2 << COUNT_BITS,即高3位为010, 所有的任务都已经终止;
5、TERMINATED: 3 << COUNT_BITS,即高3位为011, terminated()方法已经执行完成

3.3 任务的执行

execute --> addWorker --> Thread.start --> (Thread.run) --> runTask --> getTask

3.3.1 execute(Runnable command)

大致分三个步骤

1、当前运行的线程数量是否小于corePoolSize,直接尝试addWorker()
2、往阻塞队列里面放入Runnable任务
3、如果队列已经满了,直接尝试addWorker()

3.3.2 addWorker(Runnable firstTask, boolean core)

1、前置判断线程池的状态

2、通过CAS操作让ctl加1,表示运行线程数增加1个
3、构造一个Worker w,这里要特别注意构造方法里面的这行代码,this.thread = getThreadFactory().newThread(this),可以看到构造方法内,有一个Thread对象,其使用了ThreadFactory构造了一个新的线程,并且线程的runable是worker本身。
4、执行w.thread.start(),也就是说,当该线程被运行时,Worker中的run方法会被执行

3.3.3 runWorker(Worker w)

通过循环调用getTask()获取要执行的任务task

beforeExecute
task.run()
afterExecute

3.3.4 getTask()

直接贴源码了

private Runnable getTask() {    boolean timedOut = false; // 是否最后的 poll() 超时了?    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);        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;	// worker是否需要被淘汰        if ((wc > maximumPoolSize || (timed && timedOut))            && (wc > 1 || workQueue.isEmpty())) {            // 这里会让线程的数量记录减,后面的return null,会导致runWorker没有获取到数据而让run()方法走到尽头,最终当前线程结束            if (compareAndDecrementWorkerCount(c))                return null;            continue;        }        try {            // 如果需要回收一部分线程,那么超时时间keepAliveTime后拿不到就数据就继续循环调用,就可以在下一次循环的时候进行线程结束回收了;否则一直阻塞下去            Runnable r = timed ?                workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :                workQueue.take();            if (r != null)                return r;            timedOut = true;        } catch (InterruptedException retry) {            timedOut = false;        }    }}

4 任务执行,带返回值的

直接贴源码了

public 
Future
submit(Callable
task) { if (task == null) throw new NullPointerException(); RunnableFuture
ftask = newTaskFor(task); execute(ftask); return ftask;}public Future
submit(Runnable task) { if (task == null) throw new NullPointerException(); RunnableFuture
ftask = newTaskFor(task, null); execute(ftask); return ftask;}

代码比较简单,把任务封装成一个既实现Runnable, 也实现Future

的接口,这个时候就可以调用execute()进行实现了

5 参考资料

转载地址:http://boufz.baihongyu.com/

你可能感兴趣的文章
NIO蔚来 面试——IP地址你了解多少?
查看>>
NISP一级,NISP二级报考说明,零基础入门到精通,收藏这篇就够了
查看>>
NISP国家信息安全水平考试,收藏这一篇就够了
查看>>
NIS服务器的配置过程
查看>>
Nitrux 3.8 发布!性能全面提升,带来非凡体验
查看>>
NiuShop开源商城系统 SQL注入漏洞复现
查看>>
NI笔试——大数加法
查看>>
NLog 自定义字段 写入 oracle
查看>>
NLog类库使用探索——详解配置
查看>>
NLP 基于kashgari和BERT实现中文命名实体识别(NER)
查看>>
NLP 模型中的偏差和公平性检测
查看>>
Vue3.0 性能提升主要是通过哪几方面体现的?
查看>>
NLP 项目:维基百科文章爬虫和分类【01】 - 语料库阅读器
查看>>
NLP_什么是统计语言模型_条件概率的链式法则_n元统计语言模型_马尔科夫链_数据稀疏(出现了词库中没有的词)_统计语言模型的平滑策略---人工智能工作笔记0035
查看>>
NLP三大特征抽取器:CNN、RNN与Transformer全面解析
查看>>
NLP学习笔记:使用 Python 进行NLTK
查看>>
NLP度量指标BELU真的完美么?
查看>>
NLP的不同研究领域和最新发展的概述
查看>>
NLP的神经网络训练的新模式
查看>>
NLP采用Bert进行简单文本情感分类
查看>>