核心线程,非核心线程的区别你还记得吗? |
您所在的位置:网站首页 › 核心线程数量 › 核心线程,非核心线程的区别你还记得吗? |
/ 今日科技快讯 / 近日,腾讯发布了2020年第三季度业绩报告。财报显示,腾讯第三季度营收1254.5亿元,市场预估1238.29亿元,去年同期972.4亿元,同比增长29%。净利润385.4亿元,同比增长89%,市场预期308.1亿元,去年同期203.82亿元。 / 作者简介 / 一晃又到了周五啦,祝大家周末愉快哦!我们下周见! 本篇文章来自小猪快跑22的投稿,通过实例带着大家一起来分析ThreadPoolExecutor源码,相信会对大家有所帮助!同时也感谢作者贡献的精彩文章! 小猪快跑22的博客地址: https://blog.csdn.net/zhujiangtaotaise / 前言 / 其实大概1年前就想把线程池的源码完整的撸一遍了,但是看的时候太注重细节了,比如其中的CAS操作、AQS以及ReentrantLock的lock、tryLock等,结果就是跑偏了;所幸今年年初的时候有时间,就把AQS的源码、CAS的使用以及ReentrantLock等一些并发编程的源码撸了一遍,再来看线程池的源码,感觉还是非常的舒爽,哈哈哈。共勉。 讲线程池的原理之前,先得了解一下线程池中几个重要的概念。 核心线程数 (corePoolSize):核心线程的数量;它的作用可以这样理解:向线程池中添加任务,如果线程池中的线程数量小于 corePoolSize,那么直接新建线程执行任务;如果线程池中的线程数量大于corePoolSize,那么就会往 阻塞队列workQueue中添加任务,此时如果阻塞队列满了且线程池中的线程数量小于最大线程数 maximumPoolSize,那么也会新建一个线程执行任务;如果阻塞队列满且线程数量大于最大线程数maximumPoolSize,那么会执行饱和策略,默认的策略是抛弃要加入的任务。 最大线程数 (maximumPoolSize):如果阻塞队列满了,则判断线程池中的线程数量是否小于 maximumPoolSize,是则直接新建一个线程来处理任务,否则执行饱和策略。 阻塞队列**(workQueue)**:线程池中的线程数量大于核心线程的数量,则将新建的任务加入到阻塞队列。 空闲线程的存活时间 (keepAliveTime):线程空闲下来之后,线程的存活时间,超过这个时间还没有任务执行,则结束该线程。注意,这个回收只是回收非核心线程,比方说核心线程数是2,最大线程数是6,假设任务非常多,最后创建了6个线程来执行任务,最后后回收4个非核心线程,而核心线程不会回收,除非你任务设置要回收核心线程。 饱和策略 (RejectedExecutionHandler):当等待队列已满,线程数也达到最大线程数时,线程池会根据饱和策略来执行后续操作,默认的策略是抛弃要加入的任务。 下面来一张图来说明下: 线程池的几种状态 RUNNING: 运行状态,能够接受新的任务且会处理阻塞队列中的任务。 SHUTDOWN:关闭状态,不接受新任务,但是会处理阻塞队列中的任务,执行线程池的 shutDown()对应的就是此状态。 STOP:停止状态,不接受新的任务,也不会处理等待队列中的任务并且会中断正在执行的任务。调用线程池的 shutDownNow()对应的是此状态 TIDYING: 整理,即所有的任务都停止了,线程池中线程数量等于0,会调用 terminated()如果你自己实现线程池的话。 TERMINATED:结束状态,terminated()方法执行完了。 下面是一些重要的变量注释: //CAS, 它的高三位表示线程池的状态,低29位表示线程池中现有的线程数 private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)); //表示线程池线程数的bit数 private static final int COUNT_BITS = Integer.SIZE - 3; //最大的线程数量,数量是完全够用了 0001 1111 1111 1111 1111 1111 1111 1111 private static final int CAPACITY = (1 = STOP || workQueue.isEmpty()))成立,执行 decrementWorkerCount()将线程池的线程减1,所以此时线程池中线程的个数为1,然后 return null,即 runWorker方法中的while循环while (task != null || (task = getTask()) != null)结束。接着会调用 processWorkerExit(w, completedAbruptly),方法如下: private void processWorkerExit(Worker w, boolean completedAbruptly) { if (completedAbruptly) // 正常为false,不用管 decrementWorkerCount(); final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { completedTaskCount += w.completedTasks; workers.remove(w);// 从works中删除已完成的Worker } finally { mainLock.unlock(); } tryTerminate(); // **注1** int c = ctl.get(); // 线程池的状态小于STOP if (runStateLessThan(c, STOP)) { // **注2** if (!completedAbruptly) { int min = allowCoreThreadTimeOut ? 0 : corePoolSize; if (min == 0 && !workQueue.isEmpty()) min = 1; if (workerCountOf(c) >= min) return; // replacement not needed } addWorker(null, false); } } 这里就不带着分析**processWorkerExit**了。下面再带着分析5.此时核心线程Thread1执行完任务2,会再次调用 getTask方法 ,接着分析 getTask方法: 注1 :条件也是成立的,同上;执行decrementWorkerCount 使线程池的线程数减1,此时线程池中的个数等于0,然后 return null,结束 runWorker 中的 while循环。 接着就是执行 runWorker 中finally块中的processWorkerExit方法。 processWorkerExit 方法分析开始 注1:执行tryTerminate,接着分析 tryTerminate中的代码: tryTerminate分析开始: tryTerminate 中的 注1 条件不满足,因为线程池的状态是SHUT_DOWN,且workQueue是empty; 注2:条件if (workerCountOf(c) != 0)也不满足,此时线程个数等于0 注3:设置线程池的状态为TIDYING成功, 注4:ctl.set(ctlOf(TERMINATED, 0)) 设置线程池的状态为 TERMINATED tryTerminate分析结束 执行 processWorkerExit 的注2,由于此时线程池的状态为TERMINATED,所以条件不成立,核心线程对应的 runWorker方法执行完了。 到此,执行 shutDown后的,所以线程(包含核心线程和非核心线程)都回收了。 本篇主要讲了 线程池中任务的执行、线程的复用、线程的回收,以及一些case被调用到的条件。 这篇文章已经太长了,包含shutDown和shutDownNow的区别,下篇再分析啦。 / 总结 / 如果不执行线程池的shutDown方法且没有调用设置核心线程空闲存活时间,那么只会回收非核心线程,不会回收核心线程,核心线程会一直阻塞 调用shutDown方法后,所有线程都会回收,这也是为什么大多数我们调用线程池的 execute方法后,会调用shutDown方法 线程池的执行流程分析 推荐阅读: WorkManager流程分析和源码解析 我的新书,《第一行代码 第3版》已出版! 排障困难?给你的应用嵌入一个Logcat吧 欢迎关注我的公众号 学习技术或投稿 长按上图,识别图中二维码即可关注 |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |