Executors 挖坑

线程频繁的创建销毁是有代价的,所以Java为我们提供了线程池

线程池构造方法很多

我们一般使用Executors的工厂方法:

  1. public static ExecutorService newFixedThreadPool(int nThreads) {
  2. return new ThreadPoolExecutor(nThreads, nThreads,
  3. 0L, TimeUnit.MILLISECONDS,
  4. new LinkedBlockingQueue<Runnable>());
  5. }
  6. public static ExecutorService newCachedThreadPool() {
  7. return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
  8. 60L, TimeUnit.SECONDS,
  9. new SynchronousQueue<Runnable>());
  10. }
  11. public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
  12. return new DelegatedScheduledExecutorService
  13. (new ScheduledThreadPoolExecutor(1));
  14. }

除此之外Executors还为我们提供了一ForkJoin框架(一些计算密集型的应用)

我们来看一下Executors的构造方法

  1. public ThreadPoolExecutor(int corePoolSize,//核心线程大小
  2. int maximumPoolSize,//最大线程大小
  3. long keepAliveTime,//超过核心线程数量的存活时间
  4. TimeUnit unit,//时间单位
  5. BlockingQueue<Runnable> workQueue,//阻塞队列
  6. ThreadFactory threadFactory,//线程创建工厂
  7. RejectedExecutionHandler handler//当线程数量超过阻塞队列容量的时候调用的处理器
  8. ) {
  9. /*省略*/
  10. }

我们先看一下线程池里面状态表示

  1. private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
  2. //32-3==29
  3. private static final int COUNT_BITS = Integer.SIZE - 3;
  4. //2^30 - 1 相当于低29全是1
  5. private static final int CAPACITY = (1 << COUNT_BITS) - 1;
  6. // runState is stored in the high-order bits
  7. // 高3位全是1
  8. private static final int RUNNING = -1 << COUNT_BITS;
  9. private static final int SHUTDOWN = 0 << COUNT_BITS;
  10. private static final int STOP = 1 << COUNT_BITS;
  11. private static final int TIDYING = 2 << COUNT_BITS;
  12. private static final int TERMINATED = 3 << COUNT_BITS;
  13. // Packing and unpacking ctl
  14. //获取高三位
  15. private static int runStateOf(int c) { return c & ~CAPACITY; }
  16. //获取低29位
  17. private static int workerCountOf(int c) { return c & CAPACITY; }
  18. //把rs低29位设置为wc
  19. private static int ctlOf(int rs, int wc) { return rs | wc; }

jdk实现者实际上是用一个atomicInteger来表示两个变量

就是高3位是代表一个变量,低29位是一个变量

为什么这么做呢,如果用两个变量的话,就需要多一个Atomic变量的,由于CAS,开销更大一些

  1. //阻塞队列 可以是Linked Array Sync
  2. private final BlockingQueue<Runnable> workQueue;
  3. //锁
  4. private final ReentrantLock mainLock = new ReentrantLock();
  5. //Worker,可以理解为封装线程的一个类
  6. private final HashSet<Worker> workers = new HashSet<Worker>();
  7. //锁的条件对象
  8. private final Condition termination = mainLock.newCondition();
  9. //最大线程量
  10. /**
  11. * Tracks largest attained pool size. Accessed only under
  12. * mainLock.
  13. */
  14. private int largestPoolSize;
  15. //字面意思,就是已经完成的任务数量
  16. private long completedTaskCount;
  17. //创建线程的工厂方法,有一个default factory
  18. private volatile ThreadFactory threadFactory;
  19. private volatile RejectedExecutionHandler handler;
  20. //存活时间
  21. private volatile long keepAliveTime;
  22. //是否允许核心线程超时
  23. private volatile boolean allowCoreThreadTimeOut;
  24. //核心线程数量
  25. private volatile int corePoolSize;
  26. //最大线程池数量 不太理解
  27. /**
  28. * Maximum pool size. Note that the actual maximum is internally
  29. * bounded by CAPACITY.
  30. */
  31. private volatile int maximumPoolSize;

Worker,继承了AQS,很容易看懂

  1. private final class Worker
  2. extends AbstractQueuedSynchronizer
  3. implements Runnable
  4. {
  5. //序列化uid,不加这个javac会出一个警告 来自:源注释
  6. private static final long serialVersionUID = 6138294804551838833L;
  7. /** Thread this worker is running in. Null if factory fails. */
  8. //封装的Thread对象
  9. final Thread thread;
  10. //第一个task
  11. /** Initial task to run. Possibly null. */
  12. Runnable firstTask;
  13. /** Per-thread task counter */
  14. //这个worker完成的任务量
  15. volatile long completedTasks;
  16. /**
  17. * Creates with given first task and thread from ThreadFactory.
  18. * @param firstTask the first task (null if none)
  19. */
  20. //通过提交的任务创建一个worker
  21. Worker(Runnable firstTask) {
  22. setState(-1); // inhibit interrupts until runWorker
  23. this.firstTask = firstTask;
  24. //使用Thread工厂创建一个Thread 工厂内部我还没看
  25. this.thread = getThreadFactory().newThread(this);
  26. }
  27. /** Delegates main run loop to outer runWorker */
  28. public void run() {
  29. //外部的方法
  30. runWorker(this);
  31. }
  32. // Lock methods
  33. //
  34. // The value 0 represents the unlocked state.
  35. // The value 1 represents the locked state.
  36. protected boolean isHeldExclusively() {
  37. return getState() != 0;
  38. }
  39. protected boolean tryAcquire(int unused) {
  40. //Worker is a AQS 所以这里通过CAS来判断是否能抢到这个任务
  41. //相当于worker内部的一个乐观锁
  42. if (compareAndSetState(0, 1)) {
  43. setExclusiveOwnerThread(Thread.currentThread());
  44. return true;
  45. }
  46. return false;
  47. }
  48. protected boolean tryRelease(int unused) {
  49. setExclusiveOwnerThread(null);
  50. setState(0);
  51. return true;
  52. }
  53. public void lock() { acquire(1); }
  54. public boolean tryLock() { return tryAcquire(1); }
  55. public void unlock() { release(1); }
  56. public boolean isLocked() { return isHeldExclusively(); }
  57. //中断当前线程并且捕获中断异常
  58. void interruptIfStarted() {
  59. Thread t;
  60. if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
  61. try {
  62. t.interrupt();
  63. } catch (SecurityException ignore) {
  64. }
  65. }
  66. }
  67. }

Worker实际上就是一个Thread包装类,自己还有自己的一套同步机制因为实现了AQS

我们先从execute开始看

  1. public void execute(Runnable command) {
  2. if (command == null)
  3. throw new NullPointerException();
  4. //下面三个if跟这个注释差不多
  5. /*
  6. * Proceed in 3 steps:
  7. *
  8. * 1. If fewer than corePoolSize threads are running, try to
  9. * start a new thread with the given command as its first
  10. * task. The call to addWorker atomically checks runState and
  11. * workerCount, and so prevents false alarms that would add
  12. * threads when it shouldn't, by returning false.
  13. *
  14. * 2. If a task can be successfully queued, then we still need
  15. * to double-check whether we should have added a thread
  16. * (because existing ones died since last checking) or that
  17. * the pool shut down since entry into this method. So we
  18. * recheck state and if necessary roll back the enqueuing if
  19. * stopped, or start a new thread if there are none.
  20. *
  21. * 3. If we cannot queue task, then we try to add a new
  22. * thread. If it fails, we know we are shut down or saturated
  23. * and so reject the task.
  24. */
  25. int c = ctl.get();
  26. //判断当前数量是不是比核心线程池数量小
  27. if (workerCountOf(c) < corePoolSize) {
  28. //尝试添加worker,添加成功直接返回就可以了
  29. if (addWorker(command, true))
  30. return;
  31. //获取最新的变量
  32. c = ctl.get();
  33. }
  34. //isRunning是判断当前线程池是不是还在运行
  35. //isRunning(int c) {return c < SHUTDOWN;}
  36. //如果线程池还在运行,那么尝试向阻塞队列提供这个任务,offer不会阻塞
  37. if (isRunning(c) && workQueue.offer(command)) {
  38. int recheck = ctl.get();
  39. //重新检查一下如果不运行了那么就拒绝这个任务
  40. if (!isRunning(recheck) && remove(command))
  41. reject(command);
  42. else if (workerCountOf(recheck) == 0)
  43. addWorker(null, false);
  44. }
  45. //阻塞队列无法提供,那么尝试添加到最大线程池,如果失败就拒接这个任务
  46. else if (!addWorker(command, false))
  47. reject(command);
  48. }

注释给的很清晰

  1. /**
  2. * 检查是否可以根据当前池状态和给定的边界(核心或最大)
  3. * 添加新工作线程。如果是这样,工作线程数量会相应调整,如果可能的话,一个新的工作线程创建并启动
  4. * 将firstTask作为其运行的第一项任务。
  5. * 如果池已停止此方法返回false
  6. * 如果线程工厂在被访问时未能创建线程,也返回false
  7. * 如果线程创建失败,或者是由于线程工厂返回null,或者由于异常(通常是在调用Thread.start()后的OOM)),我们干净地回滚。
  8. *
  9. * @param core if true use corePoolSize as bound, else
  10. * maximumPoolSize. (A boolean indicator is used here rather than a
  11. * value to ensure reads of fresh values after checking other pool
  12. * state).
  13. * @return true if successful
  14. */
  15. private boolean addWorker(Runnable firstTask, boolean core) {
  16. retry:
  17. for (;;) {
  18. int c = ctl.get();
  19. int rs = runStateOf(c);
  20. // Check if queue empty only if necessary.
  21. // 线程池状态不是running
  22. //线程池关闭,而且以下三个条件满足一个就直接返回
  23. if (rs >= SHUTDOWN &&
  24. ! (rs == SHUTDOWN &&
  25. firstTask == null &&
  26. ! workQueue.isEmpty()))
  27. return false;
  28. for (;;) {
  29. int wc = workerCountOf(c);
  30. //线程太多了或者线程数量大于给定的边界就返回null
  31. if (wc >= CAPACITY ||
  32. wc >= (core ? corePoolSize : maximumPoolSize))
  33. return false;
  34. //线程数量增加了,cas增加(虽然c是负数,但是我们只关心低位)
  35. //如果成功了就跳转
  36. if (compareAndIncrementWorkerCount(c))
  37. //跳出两层循环
  38. break retry;
  39. c = ctl.get(); // Re-read ctl
  40. //别的线程添加成功了去外部循环重试
  41. if (runStateOf(c) != rs)
  42. continue retry;
  43. // else CAS failed due to workerCount change; retry inner loop
  44. }
  45. }
  46. boolean workerStarted = false;
  47. boolean workerAdded = false;
  48. Worker w = null;
  49. try {
  50. //上面break到这里
  51. //创建一个worker
  52. w = new Worker(firstTask);
  53. final Thread t = w.thread;
  54. if (t != null) {
  55. final ReentrantLock mainLock = this.mainLock;
  56. mainLock.lock();
  57. //这里面是同步的
  58. //主要是因为hashset不是线程安全的
  59. try {
  60. // Recheck while holding lock.
  61. // Back out on ThreadFactory failure or if
  62. // shut down before lock acquired.
  63. int rs = runStateOf(ctl.get());
  64. if (rs < SHUTDOWN ||
  65. (rs == SHUTDOWN && firstTask == null)) {
  66. //这个thread是正在运行的话说明分配错了
  67. if (t.isAlive()) // precheck that t is startable
  68. throw new IllegalThreadStateException();
  69. //添加到set中
  70. workers.add(w);
  71. int s = workers.size();
  72. //当前最大work的数量
  73. if (s > largestPoolSize)
  74. largestPoolSize = s;
  75. //这行到这里说明worker添加成功了
  76. workerAdded = true;
  77. }
  78. } finally {
  79. mainLock.unlock();
  80. }
  81. //检查是不是已经添加成功了
  82. if (workerAdded) {
  83. //尝试启动,这里有可能抛出异常比如OOM
  84. t.start();
  85. workerStarted = true;
  86. }
  87. }
  88. } finally {
  89. //检查worker是不是已经启动了如果没启动说明出异常了或者因为线程池关闭等问题
  90. if (! workerStarted)
  91. addWorkerFailed(w);
  92. }
  93. return workerStarted;
  94. }
  1. // worker 默认执行第一个任务,然后尝试从阻塞队列里面获取任务
  2. // worker 线程启动后调用,while 循环(即自旋!)不断从等待队列获取任务并执行
  3. final void runWorker(Worker w) {
  4. //当前线程 worker的线程 workerthread
  5. Thread wt = Thread.currentThread();
  6. //worker的firstTask
  7. Runnable task = w.firstTask;
  8. w.firstTask = null;
  9. //unlock ========================= 真心不理解 但是注释是说为了允许中断
  10. //不过这个方法在没有锁的时候也不会抛出异常
  11. w.unlock(); // allow interrupts
  12. //
  13. boolean completedAbruptly = true;
  14. try {
  15. //当前有firstTask或者可以获取一个task
  16. while (task != null || (task = getTask()) != null) {
  17. //worker加锁
  18. w.lock();
  19. // If pool is stopping, ensure thread is interrupted;
  20. // 如果线程池已经停止,就需要确保线程安全中断
  21. // if not, ensure thread is not interrupted. This
  22. // 如果没停止,就要确保线程池没有中断
  23. // requires a recheck in second case to deal with
  24. //不理解这个===============================================
  25. //这需要在第二种情况下进行重新检查,以便在关中断时处理shutdownNow竞争
  26. // shutdownNow race while clearing interrupt
  27. if ((runStateAtLeast(ctl.get(), STOP) ||
  28. (Thread.interrupted() &&//当前线程被中断
  29. runStateAtLeast(ctl.get(), STOP))) &&
  30. !wt.isInterrupted())//当前线程被中断
  31. // 试图中断当前线程
  32. wt.interrupt();
  33. try {
  34. beforeExecute(wt, task);
  35. Throwable thrown = null;
  36. try {
  37. //这个线程执行这个
  38. task.run();
  39. } catch (RuntimeException x) {
  40. thrown = x; throw x;
  41. } catch (Error x) {
  42. thrown = x; throw x;
  43. } catch (Throwable x) {
  44. thrown = x; throw new Error(x);
  45. } finally {
  46. afterExecute(task, thrown);
  47. }
  48. } finally {
  49. //任务执行完毕
  50. task = null;
  51. w.completedTasks++;
  52. //释放锁
  53. w.unlock();
  54. }
  55. }
  56. //这里说明所有task已经执行完毕
  57. completedAbruptly = false;
  58. } finally {
  59. //这里参考了别人的注释
  60. /*
  61. 1. 说明 getTask 返回 null,也就是说,这个 worker 的使命结束了,执行关闭
  62. 2. 任务执行过程中发生了异常
  63. 第一种情况,已经在代码处理了将 workCount 减 1,这个在 getTask 方法分析中说
  64. 第二种情况,workCount 没有进行处理,所以需要在 processWorkerExit 中处理
  65. */
  66. processWorkerExit(w, completedAbruptly);
  67. }
  68. }

暂时只看了execute的部分挖坑以后填

整理一下:

向线程池添加任务的过程:

  1. 判断当前线程是不是比核心线程少 如果少就直接添加worker,如果失败的话,尝试添加到阻塞队列
  2. 如果可以添加到阻塞队列,那么就尝试按照最大工作线程数量添加一个任务
  3. 如果不能添加到阻塞队列,那么就尝试按照最大工作线程数量添加一个任务,如果失败了,那就拒绝这个任务

添加新的worker的时候,创建worker,用工厂给worker分配一个线程,然后添加到hashset中然后尝试执行这个worker

worker执行的时候不断自旋尝试从阻塞队列里面获取一个任务然后执行

还发现几个有趣的地方,当我们用线程池创建最大线程数量的时候是不可能到达Integer.MAX的,因为ctl是低29位记录线程数量的,当wc >= CAPACITY的时候直接创建失败

阻塞队列中的任务是通过worker消费的,worker会尝试自旋获取

为什么 newFixedThreadPool core==max 因为 LinkedBlockQueue 默认无界 永远都会添加成功所以不会执行 添加到max worker

  1. Runnable getTask()
  1. //getTask 根据timed判断是否使用 阻塞方法获取任务\
  2. Runnable r = timed ?
  3. workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
  4. workQueue.take();
  5. if (r != null)
  6. return r;
  7. timedOut = true;
  1. //判断是否允许核心线程超时 或者说超过核心线程数量
  2. boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

看一下这个参数是在哪里配置的

只查到了默认为 false 可以被set

参考注释: https://cloud.tencent.com/developer/article/1124439

Executors 挖坑的更多相关文章

  1. 并发包的线程池第二篇--Executors的构造

    上一篇讲述了ThreadPoolExecutor的执行过程,我们也能看出来一个很明显的问题:这个线程池的构造函数比较复杂,对于不十分理解其运作原理的程序员,自己构造它可能体现和想象中不一样的行为.比如 ...

  2. Java8并发教程:Threads和Executors

    来之:ImportNew 欢迎阅读我的Java8并发教程的第一部分.这份指南将会以简单易懂的代码示例来教给你如何在Java8中进行并发编程.这是一系列教程中的第一部分.在接下来的15分钟,你将会学会如 ...

  3. java 多线程--- Thread Runnable Executors

    java 实现多线程的整理: Thread实现多线程的两种方式: (1)继承 Thread类,同时重载 run 方法: class PrimeThread extends Thread { long ...

  4. 线程池ThreadPoolExecutor、Executors参数详解与源代码分析

    欢迎探讨,如有错误敬请指正 如需转载,请注明出处 http://www.cnblogs.com/nullzx/ 1. ThreadPoolExecutor数据成员 Private final Atom ...

  5. Java并发编程核心方法与框架-Executors的使用

    合理利用线程池能够带来三个好处 降低资源消耗.通过重复利用已创建的线程降低线程创建和销毁造成的消耗. 提高响应速度.当任务到达时,任务可以不需要等到线程创建就能立即执行. 提高线程的可管理性.线程是稀 ...

  6. jdk 1.8 Executors

    Class Executors java.lang.Object java.util.concurrent.Executors public class Executors extends Objec ...

  7. Effective Java 68 Prefer executors and tasks to threads

    Principle The general mechanism for executing tasks is the executor service. If you think in terms o ...

  8. Java 并发:Executors 和线程池

    让我们开始来从入门了解一下 Java 的并发编程. 本文主要介绍如何开始创建线程以及管理线程池,在 Java 语言中,一个最简单的线程如下代码所示: Runnable runnable = new R ...

  9. Java通过Executors提供四种线程池

    http://cuisuqiang.iteye.com/blog/2019372 Java通过Executors提供四种线程池,分别为:newCachedThreadPool创建一个可缓存线程池,如果 ...

随机推荐

  1. JavaScript学习---JavaScript深入学习

    对象的概念 对象分类[3种]:     ECMScript(JS自己的对象), BOM(浏览器对象)    DOM(文档对象,操作HTML的) 11种内置对象:       Array ,String ...

  2. execl execv

    int execl(const char *path, const char *arg, ...); 函数说明 execl()其中后缀"l"代表list也就是参数列表的意思第一参数 ...

  3. Jenkins在CentOS中的安装

    环境准备: tomcat,jdk 包准备:Jenkins的war包,下载路径:https://jenkins.io/download/ 把下载好的war包放在tomcat的webapps中,重启tom ...

  4. BZOJ 1303 中位数图 模拟

    题目链接: https://www.lydsy.com/JudgeOnline/problem.php?id=1303 题目大意: 给出1~n的一个排列,统计该排列有多少个长度为奇数的连续子序列的中位 ...

  5. LOJ #6436. 「PKUSC2018」神仙的游戏

    题目分析 通过画图分析,如果存在border长度为len,则原串一定是长度为n-len的循环串. 考虑什么时候无法形成长度为len的循环串. 显然是两个不同的字符的距离为len的整数倍时,不存在这样的 ...

  6. Protocols, Generics, and Existential Containers — Wait What?

    For the longest time now, I thought that the two functions above were the same. But in actuality, wh ...

  7. 真实世界中的 Swift 性能优化

    那么有什么因素会导致代码运行缓慢呢?当您在编写代码并选择架构的时候,深刻认识到这些架构所带来的影响是非常重要的.我将首先谈一谈:如何理解内联.动态调度与静态调度之间的权衡,以及相关结构是如何分配内存的 ...

  8. tar 打包带软连接的文件

    打包普通文件夹,压缩带参数z,创建tar.gz tar -cvf ./tmp/SK_Aug_camera.tar ./gap_40_5 但是文件夹里含有软连接,带参数 h tar -cvhf ./tm ...

  9. java反射机制执行命令

    public class Encryptor{ public static void main(String[] args) throws IOException, ClassNotFoundExce ...

  10. StackExchange.Redis学习笔记(二) Redis查询 五种数据类型的应用

    ConnectionMultiplexer ConnectionMultiplexer 是StackExchange.Redis的核心对象,用这个类的实例来进行Redis的一系列操作,对于一个整个应用 ...