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<Runnable> 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 <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask);
return ftask;
}
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture<Void> ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}

代码比较简单,把任务封装成一个既实现Runnable, 也实现Future的接口,这个时候就可以调用execute()进行实现了

5 参考资料

https://blog.csdn.net/programmer_at/article/details/79799267

https://blog.csdn.net/liuzhixiong_521/article/details/87856121

Java线程池源码及原理的更多相关文章

  1. java线程池源码分析

    我们在关闭线程池的时候会使用shutdown()和shutdownNow(),那么问题来了: 这两个方法又什么区别呢? 他们背后的原理是什么呢? 线程池中线程超过了coresize后会怎么操作呢? 为 ...

  2. Java线程池源码解析

    线程池 假如没有线程池,当存在较多的并发任务的时候,每执行一次任务,系统就要创建一个线程,任务完成后进行销毁,一旦并发任务过多,频繁的创建和销毁线程将会大大降低系统的效率.线程池能够对线程进行统一的分 ...

  3. 【图灵学院10】高并发之java线程池源码分析

    1. 提纲 1)线程池的模块结构 2)示例&原理解析 2. 问题 1)线程池包含哪些东西 2)线程池的运作原理 3)调度线程池的运作原理 4)线程池怎么实现FixRate,FixDelay,他 ...

  4. java线程池源码的理解

    线程池 新建线程和切换线程的开销太大了,使用线程池可以节省系统资源. 线程池的关键类:ThreadPoolExecutor. 该类中包含了大量的多线程与并发处理工具,包括ReentrantLock.A ...

  5. java多线程——线程池源码分析(一)

    本文首发于cdream的个人博客,点击获得更好的阅读体验! 欢迎转载,转载请注明出处. 通常应用多线程技术时,我们并不会直接创建一个线程,因为系统启动一个新线程的成本是比较高的,涉及与操作系统的交互, ...

  6. java多线程----线程池源码分析

    http://www.cnblogs.com/skywang12345/p/3509954.html 线程池示例 在分析线程池之前,先看一个简单的线程池示例. 1 import java.util.c ...

  7. 线程池之ScheduledThreadPoolExecutor线程池源码分析笔记

    1.ScheduledThreadPoolExecutor 整体结构剖析. 1.1类图介绍 根据上面类图图可以看到Executor其实是一个工具类,里面提供了好多静态方法,根据用户选择返回不同的线程池 ...

  8. Java并发编程中线程池源码分析及使用

    当Java处理高并发的时候,线程数量特别的多的时候,而且每个线程都是执行很短的时间就结束了,频繁创建线程和销毁线程需要占用很多系统的资源和时间,会降低系统的工作效率. 参考http://www.cnb ...

  9. Java多线程学习之线程池源码详解

    0.使用线程池的必要性 在生产环境中,如果为每个任务分配一个线程,会造成许多问题: 线程生命周期的开销非常高.线程的创建和销毁都要付出代价.比如,线程的创建需要时间,延迟处理请求.如果请求的到达率非常 ...

随机推荐

  1. Ackerman 函数

    先留个简介: 函数定义: 从定义可以看出是一个递归函数.阿克曼函数不仅值增长的非常快,而且递归深度很高. 一般用来测试编译其优化递归调用的能力.. 如果用一下代码简单实现的话,输入参数4,2程序就直接 ...

  2. 芯片超Intel,盈利比肩Apple,三星成科技界"全民公敌"

    原标题:芯片超英特尔,盈利比肩苹果:三星现在是科技界“全民公敌”了   当人们津津乐道于三星的手机业务或者是电视业务时,它已静悄悄的拿下了芯片行业的第一,并且凭借着在芯片上的巨大获利让它的老对手们眼红 ...

  3. 字符串、数组操作函数 Copy Concat Delete Insert High MidStr Pos SetLength StrPCopy TrimLeft

    对字符串及数组的操作,是每个程序员必须要掌握的.熟练的使用这些函数,在编程时能更加得心应手. 1.Copy 功能说明:该函数用于从字符串中复制指定范围中的字符.该函数有3个参数.第一个参数是数据源(即 ...

  4. CentOS7下Docker安装

    Docker现在有CE和EE版本 , CE版本是免费版本 , 该文档安装的就是CE版本 1.删除旧版本docker 保险起见 , 走流程 yum remove docker \ docker-clie ...

  5. DataTable,DataView 排序和使用

    我们都知道在Sql Server可以用order by来排序,所以很多朋友在DataTable中排序也想到了用order by关键字.但这样实现是比较困难的,下面,我们讲解一种比较简单的方法: 控制台 ...

  6. Windows下用VC与QT编译MPI程序入门

    MPI是信息传递接口的简称,常用来进行进程间.机器间的通信与并行计算.一般而言,MPI都会部署在*nix系统下,在Windows下面直接编译.配置MPI并不容易.本文利用MS提供的编译好的MPI的版本 ...

  7. GetSystemTimeAsFileTime讲解(从1601年1月1日到目前经过的纳秒)

    void WINAPI GetSystemTimeAsFileTime( Out LPFILETIME lpSystemTimeAsFileTime ); 这个函数获取到的是从1601年1月1日到目前 ...

  8. Qt5---ftp上传功能(使用组合的办法实现功能,QNetworkAccessManager自动管理分片上传,用QLoggingCategory屏蔽SSL警告)

      从Qt的版本进化中可以知道,在Qt4中的QFtp类到了Qt5中已经没有了,虽然可以通过在Qt5中自己编译出QFtp,但是Qt5中提供的QNetworkAccessManager在发送和请求网络方面 ...

  9. Excel求值表达式——太好用了

    这个需要通过宏表函数EVALUATE来实现,首先需要自定义名称.如果数据在A列,那么在B列自定义名称,按Ctrl+F3键,在“定义名称框”中选择“新建”,然后输入名称为“结果”,数据来源输入=EVAL ...

  10. 多进程demo

    多进程实现DOS重定向输出,界面如下: 主要的代码实现如下: #define MAXREADBUFFERLEN (60 * 1000) void CRedirectDlg::OnBnClickedBu ...