参考:https://www.cnblogs.com/liuyun1995/p/9305273.html

ThreadPoolExecutor1 executorService1 = new ThreadPoolExecutor1(3)

线程池的线程是懒初始化的,第一个任务来了创建一下执行,执行完了等待队列,只会从队列拿任务,不会直接接受任务。第二个任务来了,不会加到队列,也不会给第一个线程去执行,而是创建一个线程执行,第二个线程执行完了等待队列。第三个任务来了,即使前面2个线程执行完了,也不会给前面2个线程执行,而是创建第三个线程去执行,执行完了就等队列。第四个任务来了,不会直接给线程去执行,而是加到队列,让前3个线程去队列取任务。

Worker里面封装了这个任务,并且里面有一个线程池的线程,总共3个Worker,3个任务(3个第一个任务),3个线程。线程run时候就开一个线程去执行Worker里的第一个任务,线程Thread run的时候,是开一个线程池的线程,然后Thread里面的Runnable的run方法在这个线程里面执行,所以Worker要设置成这个Thread的Runnable,就是Worker的run方法在这个线程里面执行(转调外部类的runWorker方法,把worker传进去)runWorker方法就在这个线程里面执行。Worker里面仅仅保存的是这个worker的第一个任务,第一个任务执行完会死循环执行queue队列的任务。

Worker是作为一个Runnable存在于线程池的线程Thread中的,所有Worker的方法都在这个线程中执行(多个线程执行Worker类的相同方法)。runWorker执行的时候,worker w成为了这个线程的局部变量,w.lock();w.unlock(); 多线程执行同一个方法不要紧,只要不是使用共享变量就没事。由于w不是共享变量,一个线程一个局部变量w,所以不会抢锁一个线程的Worler w不会跑到另一个线程里面去,所以是单线程访问的。

executorService.execute(new Runnable() {} = firstTask);
Worker w = new Worker(firstTask);
Worker(Runnable firstTask) {
setState(-);
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}

一个线程池线程中一个Runnable(Worker),这个Runnable(Worker)不仅仅执行第一个任务还执行队列的任务,这个Runnable一直不会退出。

所有的任务都在这个线程里面执行,所有的任务都由这个worker执行。

要想w.lock();w.unlock();有线程抢锁,那么一个worker就要被设置到多个线程池的线程去,一个Worker成为多个线程的Runnable,多个线程同时执行时候,这一个Runnable在多个线程中执行,成为多个线程的局部变量w,w.lock();w.unlock();就会出现多线程抢锁。但是不行,

线程跟worker是一对一的关系,通过Runnable建立起来的。

其他线程调用中断interrupt相关方法时候,会从workers中获取一个worker,此时worker w就存在于另一个线程中了,就会多个线程使用这个worker w了。此时锁就有意义了。

Worker初始化时候,只会在一个线程池的线程中,是单线程。但是其他线程可以从workers里面获取这个worker,就是多线程访问。

线程池的所有线程全保存在workers里面的worker里面的thread上面。

超过3个任务进来, 不会创建线程,而是直接丢到队列里面去,然后流程执行完。

Worker继承AQS也就是一个ReentantLock,不同的线程调用worker w的lock和unlock方法可以实现锁的功能,不同的线程可以锁住不同的代码段。


当一个任务提交至线程池之后,

1. 线程池首先判断核心线程池里的线程是否已经满了。如果不是,则创建一个新的工作线程来执行任务。否则进入2.

2. 判断工作队列是否已经满了,倘若还没有满,将线程放入工作队列。否则进入3.

3. 判断线程池里的线程是否都在执行任务。如果不是,则创建一个新的工作线程来执行。如果线程池满了,则交给饱和策略来处理任务。

RUNNING:111   SHUTDOWN:000    STOP:001    TIDYING:010    TERMINATED:011

SHUTDOWN = 0,STOP|TIDYING|TERMINATED > 0,RUNNING < 0

新建一个线程池时它的状态为Running,这时它不断的从外部接收并处理任务,当处理不过来时它会把任务放到任务队列中;

之后我们可能会调用shutdown()来终止线程池,这时线程池的状态从Running转为Shutdown,它开始拒绝接收从外部传过来的任务,但是会继续处理完任务队列中的任务;

我们也可能调用shutdownNow()来立刻停止线程池,这时线程池的状态从Running转为Stop,然后它会快速排空任务队列中的任务并转到Tidying状态,处于该状态的线程池需要执行terminated()来做相关的扫尾工作,

执行完terminated()之后线程池就转为Terminated状态,表示线程池已终止。这些状态的转换图如下所示。

  • RUNNING:接受新任务并且处理阻塞队列里的任务
  • SHUTDOWN:拒绝新任务但是处理阻塞队列里的任务
  • STOP:拒绝新任务并且抛弃阻塞队列里的任务同时会中断正在处理的任务
  • TIDYING:所有任务都执行完(包含阻塞队列里面任务)当前线程池活动线程为0,将要调用terminated方法
  • TERMINATED:终止状态。terminated方法调用完成以后的状态

线程池状态转换

RUNNING -> SHUTDOWN

显式调用shutdown()方法, 或者隐式调用了finalize()方法

(RUNNING or SHUTDOWN) -> STOP

显式调用shutdownNow()方法

SHUTDOWN -> TIDYING

当线程池和任务队列都为空的时候

STOP -> TIDYING

当线程池为空的时候

TIDYING -> TERMINATED

当 terminated() hook 方法执行完成时候

corePoolSize:corePoolSize –> workQueue –> maximumPoolSize。如果运行的线程少于 corePoolSize,则创建新线程来处理任务,即使线程池中的其他线程是空闲的;当线程池中的线程数量大于等于 corePoolSize 且小于 maximumPoolSize,则只有当workQueue满时才创建新的线程去处理任务;当运行的线程数量大于等于maximumPoolSize,这时如果workQueue已经满了,则通过handler所指定的策略来处理任务;一般来说都会把corePoolSize和maximumPoolSize,设置为一样,这样的话,能够减少线程创建的消耗,直接往阻塞队列中取就可以了.

如果采用无界队列,那么maximumPoolSize将会失去作用,此时一般corePoolSize和maximumPoolSize,设置为一样;

keepAliveTime:线程池维护线程所允许的空闲时间。当线程池中的线程数量大于corePoolSize的时候,如果这时没有新的任务提交,核心线程外的线程不会立即销毁,而是会等待,直到等待的时间超过了keepAliveTime;


首先是RUNNING状态,然后进入SHUTDOWN和STOP状态,进入SHUTDOWN和STOP状态还有去调整(处理队列任务),调整完最后进入TERMINATED状态。

ctl从11100000000000000000000000000000=-536870912开始,慢慢加1,一直越来越大,最后=111111111111111111111111=-1

worker线程数量最大2^29-1=536870911个,就不能再增加了,所以ctl的范围是(-536870912,-1)一直小于0。

当线程池状态改变时候,改变的是前3位,后29位不变,处于别的状态时候继续使用,原来是1110001101010101010(RUNNING状态),变成SHUTDWON之后ctl是0000001101010101010(SHUTDOWN态),变成STOP之后是0010001101010101010(STOP状态),变成TIDYING后是0100001101010101010(TIDYING态),变成TERMINATED后是0110001101010101010(TERMINATED态)

RUNNING值 < SHUTDOWN值 < STOP值 < TIDYING值 < TERMINATED值,大小顺序,基本符合状态的流转过程。

RUNNING值 <= RUNNING态ctl值 < SHUTDOWN值 <= SHUTDOWN态ctl值 < STOP值 <= STOP态ctl值 < TIDYING值 <= TIDYING态ctl值 < TERMINATED值 <=TERMINATED态ctl值。

每个状态的ctl值大于等于这个状态的标称值,小于下一个标称值。标称值是全0的临界值。rs后面全是0,ctl后面不是0。rs可以用=,ctl不能用=,ctl只能用大于小于。ctl和rs用大于小于判断得到的状态是一个区间值,是多个可能的值。

1.处于正常状态的ctl,ctl=111xxxxxxxx,rs=runStateOf(ctl)=RUNNING=111 0000000000000000,RUNNING<=ctl<SHUTDOWN

2.处于关闭状态的ctl,ctl=000xxxxxxxx,rs=runStateOf(ctl)=SHUTDOWN=000 0000000000000000,SHUTDOWN<=ctl<STOP

3.处于停止状态的ctl,ctl=001xxxxxxxx,rs=runStateOf(ctl)=STOP=001 0000000000000000 ,STOP<=ctl<TIDYING

4.处于调整中的ctl,ctl=010xxxxxxxx,rs=runStateOf(ctl)=TIDYING=010 0000000000000000,TIDYING<=ctl<TERMINATED

5.处于调整完毕的ctl,ctl=011xxxxxxxx,rs=runStateOf(ctl)=TERMINATED=011 0000000000000000 ,TERMINATED<=ctl<=2^31-1

rs只能等于111 0000000000000000(RUNNING),000 0000000000000000(SHUTDOWN),001 0000000000000000(STOP),010 0000000000000000(TIDYING),011 0000000000000000(TERMINATED)

rs>=RUNNING,ctl,rs有5种状态。

rs>=SHUTDOWN,ctl,rs有4种状态。

rs>=STOP,ctl,rs有3种状态。

rs>=TIDYING,ctl,rs有2种状态。

rs>=TERMINATED,ctl,rs有1种状态。

ctl>=RUNNING,ctl,rs有5种状态。

ctl>=SHUTDOWN,ctl,rs有4种状态。

ctl>=STOP,ctl,rs有3种状态。

ctl>=TIDYING,ctl,rs有2种状态。

ctl>=TERMINATED,ctl,rs有1种状态。

int rs = runStateOf(c);//前3位不变,后面29位=0

//c处于RUNNING状态,c=111xxxxxxxxx,rs=111 0000000000000000(RUNNING)

//c处于SHUTDOWN状态,c=000xxxxxxxxx,rs=000 0000000000000000(SHUTDOWN)

//c处于STOP状态,c=001xxxxxxxxx,rs=001 0000000000000000(STOP)

//c处于TIDYING状态,c=010xxxxxxxxx,rs=010 0000000000000000(TIDYING)

//c处于TERMINATED状态,c=011xxxxxxxxx,rs=011 0000000000000000(TERMINATED)

正常运行小于0,SHUTDOWN=0,其他STOP,TIDYING,TERMINATED都是大于0

//C的低29位,跟00011111 11111111 11111111 11111111比较,低29位表示线程池中线程数,最大2^29-1。
private static int workerCountOf(int c) {//对2^29取余,ctl加了多少次1,最大加2^29-1次。表示多少个worker已经在运行了。
return c & CAPACITY;//00011111 11111111 11111111 11111111
} // C的高3位,高3位表示线程池的运行状态。111RUNNING:运行中,接受新任务处理队列中的任务。
//000SHUTDOWN:不接收新任务处理队列中的任务; 001STOP:不接收新任务也不处理队列中的任务还中断正在运行的任务;
//010TIDYING:所有的任务都已经终止;011TERMINATED:terminated()方法已经执行完成
private static int runStateOf(int c) {
return c & ~CAPACITY;//11100000 00000000 00000000 00000000
}

ThreadPoolExecutor源码1的更多相关文章

  1. Java并发包源码学习之线程池(一)ThreadPoolExecutor源码分析

    Java中使用线程池技术一般都是使用Executors这个工厂类,它提供了非常简单方法来创建各种类型的线程池: public static ExecutorService newFixedThread ...

  2. ThreadPoolExecutor系列<三、ThreadPoolExecutor 源码解析>

    本文系作者原创,转载请注明出处:http://www.cnblogs.com/further-further-further/p/7681826.html 在源码解析前,需要先理清线程池控制的运行状态 ...

  3. ThreadPoolExecutor系列三——ThreadPoolExecutor 源码解析

    ThreadPoolExecutor 源码解析 本文系作者原创,转载请注明出处:http://www.cnblogs.com/further-further-further/p/7681826.htm ...

  4. 【Java并发编程】21、线程池ThreadPoolExecutor源码解析

    一.前言 JUC这部分还有线程池这一块没有分析,需要抓紧时间分析,下面开始ThreadPoolExecutor,其是线程池的基础,分析完了这个类会简化之后的分析,线程池可以解决两个不同问题:由于减少了 ...

  5. ThreadPoolExecutor源码解读

    1. 背景与简介 在Java中异步任务的处理,我们通常会使用Executor框架,而ThreadPoolExecutor是JUC为我们提供的线程池实现. 线程池的优点在于规避线程的频繁创建,对线程资源 ...

  6. ThreadPoolExecutor 源码阅读

    目录 ThreadPoolExecutor 源码阅读 Executor 框架 Executor ExecutorService AbstractExecutorService 构造器 状态 Worke ...

  7. Java并发之线程池ThreadPoolExecutor源码分析学习

    线程池学习 以下所有内容以及源码分析都是基于JDK1.8的,请知悉. 我写博客就真的比较没有顺序了,这可能跟我的学习方式有关,我自己也觉得这样挺不好的,但是没办法说服自己去改变,所以也只能这样想到什么 ...

  8. 【详解】ThreadPoolExecutor源码阅读(三)

    系列目录 [详解]ThreadPoolExecutor源码阅读(一) [详解]ThreadPoolExecutor源码阅读(二) [详解]ThreadPoolExecutor源码阅读(三) 线程数量的 ...

  9. 【详解】ThreadPoolExecutor源码阅读(二)

    系列目录 [详解]ThreadPoolExecutor源码阅读(一) [详解]ThreadPoolExecutor源码阅读(二) [详解]ThreadPoolExecutor源码阅读(三) AQS在W ...

  10. 【详解】ThreadPoolExecutor源码阅读(一)

    系列目录 [详解]ThreadPoolExecutor源码阅读(一) [详解]ThreadPoolExecutor源码阅读(二) [详解]ThreadPoolExecutor源码阅读(三) 工作原理简 ...

随机推荐

  1. PIE SDK加载WMS服务数据

    1.   功能简介 WMS服务,WMS是OGC标准中比较简单也是比较重要的标准之一.它全称是“Web Map Service”(网络地图服务):利用具有地理空间位置信息的数据制作地图.其中将地图定义为 ...

  2. 今天是JAVA诞生日

    今天是JAVA诞生日,祝贺!!! 1995年5月23日,Sun公司在Sun world会议上正式发布Java和HotJava浏览器,Java诞生. https://baike.baidu.com/it ...

  3. new的原理及实现

    new的过程 // new运算的过程 /** * 1.创建一个空对象: * 2.该空对象的原型指向构造函数(链接原型):将构造函数的 prototype 赋值给对象的 __proto__属性: * 3 ...

  4. android studio学习----创建模拟器

    建议在创建模拟器前把 SDK Manager 中的 Tools.Extras 都更新到最新. 如何弹出下面的各个图,首先直接点击  运行  然后会选择  launcher ,点击那个 ...就出来了 ...

  5. MySQL 错误代码:2003 idea错误:ERROR DruidDataSource:1846 - create connection error

    idea项目一启动就报错: 20:01:13,047 ERROR DruidDataSource:1846 - create connection error com.mysql.jdbc.excep ...

  6. k8s krew 插件管理工具

    参考:https://github.com/kubernetes-sigs/krew https://int32bit.me/2019/12/05/%E5%88%86%E4%BA%AB%E5%87%A ...

  7. JMETER 使用断言

    断言概念 断言就是在执行某个请求后,根据返回的结果,判断返回是否正确,如果不正确,则表示事务失败. 添加断言 启动流程时返回的数据是一个 json对象,结构为 {success:true,msg:&q ...

  8. Nginx 核心配置-根目录root指令与别名alias指令实战案例

    Nginx 核心配置-根目录root指令与别名alias指令实战案例 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.试验环境说明 1>.虚拟机环境说明 [root@nod ...

  9. V4L2视频采集原理

    一.简介 Video for Linuxtwo(Video4Linux2)简称V4L2,是V4L的改进版.V4L2是linux操作系统下用于采集图片.视频和音频数据的API接口,配合适当的视频采集设备 ...

  10. Mybatis-plus中如何排除非表字段的三种方式

    1.transient关键字 2.使用静态变量(static) 3.TableField(exit=false) 这三种方式可以在使用的过程中,是这个对象中的属性不被序列化.(直接被忽略)