参考: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. Vue配置路由和传参方式及路由守卫!

    安装路由 npm i vue-router -S 引入路由 import VueRouter form VueRouter 注入路由模块 Vue.use(VueRouter) 定义路由匹配规则 let ...

  2. Qt Examples - Boxes (在Qt场景视图中结合OpenGL渲染)

    QT自带例程Boxes使用QT Graphics View框架实现了2D图形和3D图形的混合渲染,综合性比较强,整合知识较多,值得学习. 可以使用鼠标通过以下方式控制演示中的元素: 按住鼠标左键的同时 ...

  3. W3C推进SVG规范Ver1.1(中文译稿)—Part I

    转自:http://www.gispark.com/html/GISarticle/2006/1215/826.html Scalable Vector Graphics (SVG) 1.1 Spec ...

  4. Vue+element 修改样式的scoped穿透方法

    我们在修改element的一些样式的时候,在加了scoped的时候会不起作用,下面是解决方案: 解决方法:起一个类名将页面包裹起来,后面加 /deep/ <style scoped> 1 ...

  5. android studio学习---模板

    Android Studio还为开发人员提供多种模板选项,从而大大提升开发速度.这些模板能自动创建Activity以及必要的XML文件.大家还可以利用这些模板创建出较为基础的Android应用程序,并 ...

  6. Maven打包时出现“Show Console View”错误弹出框,错误详情为“An internal error has occurred. java.lang.NullPointerException”的解决方法

    今天为项目打包时出现了下面的错误提示: 打开Details里面写的是“An internal error has occurred. java.lang.NullPointerException”.在 ...

  7. Linux源码编译nginx

    1.安装nginx 安装编译工具及库文件 yum -y install make zlib zlib-devel gcc-c++ libtool openssl openssl-devel 首先要安装 ...

  8. 第11节-BLE协议HCI层的硬件接口

    本篇博客由韦东山视频整理所得 如何控制链路层让其发出广播包.数据包?通过HCI层向它发出命令,也可以通过ATT层.L2CAP层向LL层发出数据. 学习资料: 蓝牙协议core_v5.0.pdf < ...

  9. 07-cmake语法-MATCHES

    如果给定的字串或变量值域给定的正则表达式匹配的话,表达式返回真. IF (CMAKE_SYSTEM_NAME MATCHES "Linux") MESSAGE(STATUS &qu ...

  10. mysql之drop、truncate和delete的区别

    今天在整理mysql数据库笔记的时候突然想到一个问题,就是drop.truncate和delete的区别,乍一看三者都是有删除的功能,但是具体来看还是有很多区别的.我先把这三个的作用简单说一下,有前辈 ...