资讯专栏INFORMATION COLUMN

Java线程池

darkerXi / 1207人阅读

摘要:线程池概述什么是线程池就是将多个线程放在一个池子里面所谓池化技术然后需要线程的时候不是创建一个线程而是从线程池里面获取一个可用的线程然后执行我们的任务线程池的优势降低资源消耗通过重复利用已创建的线程降低线程创建和消耗提供响应速度当任务到达时

线程池概述

什么是线程池
就是将多个线程放在一个池子里面(所谓池化技术),然后需要线程的时候不是创建一个线程,而是从线程池里面获取一个可用的线程,然后执行我们的任务.

线程池的优势

降低资源消耗,通过重复利用已创建的线程降低线程创建和消耗

提供响应速度,当任务到达时,任务可以不需要等到线程创建就立即执行

提高线程的可管理性,线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配、调优和监控.

创建一个线程池并提交线程任务

Java线程池最核心的类是ThreadPoolExecutor,查看ThreadPoolExecutor类关系继承图如下:

查看Executor接口可以通过execute方法进行提交任务
查看ExecutorService接口可以通过submit进行提交任务
所以ThreadPoolExecutor可以使用上述两种方式提交任务

ThreadPoolExecutor源码解析 类的结构

ThreadPoolExecutor的核心内部类为Worker,其对资源进行了复用,减少了创建线程的开销,而其他的AbortPolicy等则是RejectedExecutionHandler接口的各种拒绝策略类

当使用线程池并且使用有界队列的时候,如果队列满了,任务添加到线程池就会有问题,针对这个问题Java线程池提供了以下拒绝策略:

AbortPolicy:使用该策略时,如果线程池队列满了,丢掉这个任务并且抛出RejectedExecutionException异常

DiscardPolicy: 如果线程池队列满了,会直接丢掉这个任务并且不会有任何异常

DiscardOldestPolicy: 如果线程池队列满了,会将最老的(即最早进入队列的)任务删除掉并腾出队列空间,再尝试将任务加入队列

CallerRunsPolicy:如果任务添加到线程池失败,那么主线程会自己去执行该任务,不会去等待线程池的任务去执行

自定义:如果以上策略不符合业务场景,那么可以自己定义拒绝策略,只要实现RejectedExecutionHandler接口,并且实现rejectedExecution方法就可以了

由于核心内部类是worker,而且worker简易,先解析worker:

Worker类源码解析 类继承关系

</>复制代码

  1. private final class Worker extends AbstractQueuedSynchronizer implements Runnable

可知:Worker类继承了AQS抽象类,实现了Runnable接口,重写了AQS的一些方法,对应的Runnable接口可以创建线程的动作

类属性

</>复制代码

  1. private final class Worker
  2. extends AbstractQueuedSynchronizer
  3. implements Runnable
  4. {
  5. /**
  6. * This class will never be serialized, but we provide a
  7. * serialVersionUID to suppress a javac warning.
  8. */
  9. // 版本号
  10. private static final long serialVersionUID = 6138294804551838833L;
  11. /** Thread this worker is running in. Null if factory fails. */
  12. // worker 所对应的线程
  13. final Thread thread;
  14. /** Initial task to run. Possibly null. */
  15. // worker初始化任务,默认第一个任务
  16. Runnable firstTask;
  17. /** Per-thread task counter */
  18. // 每个线程任务计数器,记录已完成任务数量
  19. volatile long completedTasks;

说明:

</>复制代码

  1. 1.Thread类型的thread属性用来封装worker,对应形成一个线程
  2. 2.Runnable类型的firstTask其表示该worker包含的runnable对象,即用户自定义的Runnable
  3. 3.volatile修饰的long类型的completedTasks表示已完成的任务数量
类构造函数

</>复制代码

  1. Worker(Runnable firstTask) {
  2. // AQS的状态设置为-1,进行抑制中断直到 runWorker
  3. setState(-1); // inhibit interrupts until runWorker
  4. // 初始化第一个任务
  5. this.firstTask = firstTask;
  6. // 根据当前worker,初始化线程
  7. this.thread = getThreadFactory().newThread(this);
  8. }

进行构造worker对象,初始化对应的属性

worker核心函数分析

</>复制代码

  1. /** Delegates main run loop to outer runWorker */
  2. // 重写Runnable的run方法,并将run方法交给外部的runWorker
  3. public void run() {
  4. runWorker(this);
  5. }
  6. // Lock methods
  7. //
  8. // The value 0 represents the unlocked state.
  9. // The value 1 represents the locked state.
  10. // 是否被独占,0表示未被独占,1表示被独占
  11. protected boolean isHeldExclusively() {
  12. return getState() != 0;
  13. }
  14. // 尝试获取方法
  15. protected boolean tryAcquire(int unused) {
  16. // CAS方法设置State状态值
  17. if (compareAndSetState(0, 1)) {
  18. // 设置独占线程
  19. setExclusiveOwnerThread(Thread.currentThread());
  20. return true;
  21. }
  22. return false;
  23. }
  24. // 尝试释放
  25. protected boolean tryRelease(int unused) {
  26. // 设置独占线程为null
  27. setExclusiveOwnerThread(null);
  28. // 设置状态为0
  29. setState(0);
  30. return true;
  31. }
  32. // 获取锁
  33. public void lock() { acquire(1); }
  34. // 尝试获取锁
  35. public boolean tryLock() { return tryAcquire(1); }
  36. // 是否锁
  37. public void unlock() { release(1); }
  38. // 是否被独占
  39. public boolean isLocked() { return isHeldExclusively(); }
  40. // 中断线程操作
  41. void interruptIfStarted() {
  42. Thread t;
  43. // 当AQS状态>=0并且worker对象的线程不为null并且该线程没有被中断
  44. if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
  45. try {
  46. // 中断线程
  47. t.interrupt();
  48. } catch (SecurityException ignore) {
  49. }
  50. }
  51. }
ThreadPoolExecutor类的属性

</>复制代码

  1. public class ThreadPoolExecutor extends AbstractExecutorService {
  2. // 线程池的控制状态(用来表示线程池的运行状态--高3位和运行的worker数量--低29位)
  3. private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
  4. // 29位的偏移量
  5. private static final int COUNT_BITS = Integer.SIZE - 3;
  6. // 最大容量 2^29-1
  7. private static final int CAPACITY = (1 << COUNT_BITS) - 1;
  8. // runState is stored in the high-order bits
  9. // 线程运行状态,总共5种状态,高3位表示
  10. private static final int RUNNING = -1 << COUNT_BITS;
  11. private static final int SHUTDOWN = 0 << COUNT_BITS;
  12. private static final int STOP = 1 << COUNT_BITS;
  13. private static final int TIDYING = 2 << COUNT_BITS;
  14. private static final int TERMINATED = 3 << COUNT_BITS;
  15. // 对ctl进行装箱和拆箱动作
  16. // 拆分运行状态
  17. private static int runStateOf(int c) { return c & ~CAPACITY; }
  18. // 拆分线程数量
  19. private static int workerCountOf(int c) { return c & CAPACITY; }
  20. // 运行状态和线程数量组合
  21. private static int ctlOf(int rs, int wc) { return rs | wc; }
  22. /*
  23. * Bit field accessors that don"t require unpacking ctl.
  24. * These depend on the bit layout and on workerCount being never negative.
  25. */
  26. // 判断当前的运行状态是否在s这个标准状态之下
  27. private static boolean runStateLessThan(int c, int s) {
  28. return c < s;
  29. }
  30. // 判断当前的运行状态是否在s这个标准状态之上
  31. private static boolean runStateAtLeast(int c, int s) {
  32. return c >= s;
  33. }
  34. // 判断是否为运行状态
  35. private static boolean isRunning(int c) {
  36. return c < SHUTDOWN;
  37. }
  38. /**
  39. * Attempts to CAS-increment the workerCount field of ctl.
  40. * 尝试以CAS方式增加ctl里的workerCount字段
  41. */
  42. private boolean compareAndIncrementWorkerCount(int expect) {
  43. return ctl.compareAndSet(expect, expect + 1);
  44. }
  45. /**
  46. * Attempts to CAS-decrement the workerCount field of ctl.
  47. * 尝试以CAS方式递减ctl里的workerCount字段
  48. */
  49. private boolean compareAndDecrementWorkerCount(int expect) {
  50. return ctl.compareAndSet(expect, expect - 1);
  51. }
  52. /**
  53. * 递减ctl的workcount字段,仅仅在线程突然终止时才调用(具体见processWorkerExit)
  54. * 其他递减在getTask内执行
  55. */
  56. private void decrementWorkerCount() {
  57. do {} while (! compareAndDecrementWorkerCount(ctl.get()));
  58. }
  59. /**
  60. * 阻塞队列:用于保存任务和移交任务给工作线程
  61. * 不要求workQueue执行poll()方法返回null去判断workQueue的isEmpty()
  62. */
  63. private final BlockingQueue workQueue;
  64. /**
  65. * 可重入锁:持有锁才可以访问workers集合和相关的记录
  66. * 虽然可以使用并行集,但是通常最好使用锁;原因是序列化
  67. * interruptIdleWorkers需避免不需要的interrupt storms,特别是shutdown期间
  68. * 否则退出线程将同时中断那些尚未中断的.
  69. */
  70. private final ReentrantLock mainLock = new ReentrantLock();
  71. /**
  72. * 存放工作线程集合
  73. * Set集合包含线程池中所有线程,当持有mainLock就可以被访问
  74. */
  75. private final HashSet workers = new HashSet();
  76. /**
  77. * 终止条件
  78. */
  79. private final Condition termination = mainLock.newCondition();
  80. /**
  81. * 最大线程池容量(仅在mainLock下可以访问)
  82. */
  83. private int largestPoolSize;
  84. /**
  85. * 已完成任务数量.(仅在工作线程终止时更新,并且持有mainLock)
  86. */
  87. private long completedTaskCount;
  88. /*
  89. * 下方的所有用户控制参数都被声明为volatile,以致于操作于最新的值
  90. * 但是不需要锁定,因为没有内部变量依赖它们在其他操作上同步修改
  91. */
  92. /**
  93. * 线程工厂:所有线程都是通过工厂创建(通过addworker)
  94. * 所有调用必须准备好addworker失败情况(如限制线程数量的策略时候),
  95. */
  96. private volatile ThreadFactory threadFactory;
  97. /**
  98. * 在失败时(执行饱和或关机)调用的处理程序
  99. */
  100. private volatile RejectedExecutionHandler handler;
  101. /**
  102. * 线程没有任务执行时最多保持多久时间会终止
  103. * 线程在存在corePoolSize或allowCoreThreadTimeOut时使用此超时
  104. */
  105. private volatile long keepAliveTime;
  106. /**
  107. * 是否运行核心线程超时机制
  108. */
  109. private volatile boolean allowCoreThreadTimeOut;
  110. /**
  111. * 线程池大小
  112. */
  113. private volatile int corePoolSize;
  114. /**
  115. * 最大线程池大小(受限于容量)
  116. */
  117. private volatile int maximumPoolSize;
  118. /**
  119. * 默认拒绝执行策略
  120. */
  121. private static final RejectedExecutionHandler defaultHandler =
  122. new AbortPolicy();
  123. /**
  124. * shutdown和shutdownNow调用时所需的权限
  125. */
  126. private static final RuntimePermission shutdownPerm =
  127. new RuntimePermission("modifyThread");
  128. /* 执行finalizer时要使用的上下文 */
  129. private final AccessControlContext acc;

着重讲解下线程池的运行状态:
1.RUNNING:接受新任务并且处理已经进入阻塞队列的任务
2.SHUTDOWN:不接受新任务,但是处理已经进入阻塞队列的任务
3.STOP:不接受新任务,不处理已经进入阻塞队列的任务并且中断正在运行任务
4.TIDYING:所有任务都已经终止,workerCount为0,线程转化为TIDYING状态并且调用terminated钩子函数
5.terminated钩子函数已经运行完成

</>复制代码

  1. private static final int RUNNING = -1 << COUNT_BITS;
  2. private static final int SHUTDOWN = 0 << COUNT_BITS;
  3. private static final int STOP = 1 << COUNT_BITS;
  4. private static final int TIDYING = 2 << COUNT_BITS;
  5. private static final int TERMINATED = 3 << COUNT_BITS;

runState单调增加,不一定要命中每个状态:

</>复制代码

  1. RUNNING -> SHUTDOWN:调用SHUTDOWN()时,可能隐式在最后调用finalize()
  2. (RUNNING or SHUTDOWN) -> STOP:调用shutdownNow()
  3. SHUTDOWN -> TIDYING:当队列和线程池都为空时
  4. STOP -> TIDYING:当线程池为空时
  5. TIDYING -> TERMINATED:当terminated()钩子方法已经完成
ThreadPoolExecutor类的构造函数

ThreadPoolExecutor类总共有四个构造函数,但是前面三个都是特例最终调的都是最后一个,咱先解析每个构造函数再统一分析好它每一个参数的意思
1.ThreadPoolExecutor(int, int, long, TimeUnit, BlockingQueue)

</>复制代码

  1. public ThreadPoolExecutor(int corePoolSize,
  2. int maximumPoolSize,
  3. long keepAliveTime,
  4. TimeUnit unit,
  5. BlockingQueue workQueue) {
  6. this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), defaultHandler);
  7. }

说明:该构造函数默认的线程工厂及拒绝执行策略去创建ThreadPoolExecutor
2.ThreadPoolExecutor(int, int, long, TimeUnit, BlockingQueue, ThreadFactory)

</>复制代码

  1. public ThreadPoolExecutor(int corePoolSize,
  2. int maximumPoolSize,
  3. long keepAliveTime,
  4. TimeUnit unit,
  5. BlockingQueue workQueue,
  6. ThreadFactory threadFactory) {
  7. this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, defaultHandler);
  8. }

说明:该构造函数只给出默认的拒绝执行策略
3.ThreadPoolExecutor(int, int, long, TimeUnit, BlockingQueue, RejectedExecutionHandler)

</>复制代码

  1. public ThreadPoolExecutor(int corePoolSize,
  2. int maximumPoolSize,
  3. long keepAliveTime,
  4. TimeUnit unit,
  5. BlockingQueue workQueue,
  6. RejectedExecutionHandler handler) {
  7. this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), handler);
  8. }

说明:该构造函数只给出默认的线程工厂
4.ThreadPoolExecutor(int, int, long, TimeUnit, BlockingQueue, ThreadFactory, RejectedExecutionHandler)

</>复制代码

  1. public ThreadPoolExecutor(int corePoolSize,
  2. int maximumPoolSize,
  3. long keepAliveTime,
  4. TimeUnit unit,
  5. BlockingQueue workQueue,
  6. ThreadFactory threadFactory,
  7. RejectedExecutionHandler handler) {
  8. // 线程池大小不能小于0 || 最大容量不能小于0 || 最大容量不能小于线程池大小 || keepAliveTime不能小于0
  9. if (corePoolSize < 0 || maximumPoolSize <= 0 || maximumPoolSize < corePoolSize || keepAliveTime < 0)
  10. throw new IllegalArgumentException();
  11. if (workQueue == null || threadFactory == null || handler == null)
  12. throw new NullPointerException();
  13. // 初始化相应的属性数据
  14. this.acc = System.getSecurityManager() == null ? null : AccessController.getContext();
  15. this.corePoolSize = corePoolSize;
  16. this.maximumPoolSize = maximumPoolSize;
  17. this.workQueue = workQueue;
  18. this.keepAliveTime = unit.toNanos(keepAliveTime);
  19. this.threadFactory = threadFactory;
  20. this.handler = handler;
  21. }

corePoolSize:线程池大小,在创建线程池后,默认情况下线程池中并没有任何线程,而是等到有任务到来后才创建线程去执行任务,除非调用了prestartAllCoreThreads()或者prestartCoreThread()方法,就会预创建线程,即在没有任务到来之前就创建corePoolSize个线程或一个线程.默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中

maximumPoolSize:线程池最大线程数,表示线程池中最多创建多少个线程

keepAliveTime:表示线程没有任务执行时最多保持多久时间会终止.默认情况下只有当线程池中的线程数大于corePoolSize时,KeepAliveTime才会起作用,直到线程池中的线程数不大于corePoolSize,即当线程池中的线程数大于CorePoolSize时,如果一个线程空闲的时间达到keepAliveTime则会终止,直到线程池中的线程数不超过corePoolSize.但是如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0

unit: 参数keepAliveTime的时间单位,有7种取值,默认为纳秒

</>复制代码

  1. TimeUnit.DAYS; //天
  2. TimeUnit.HOURS; //小时
  3. TimeUnit.MINUTES; //分钟
  4. TimeUnit.SECONDS; //秒
  5. TimeUnit.MILLISECONDS; //毫秒
  6. TimeUnit.MICROSECONDS; //微妙
  7. TimeUnit.NANOSECONDS; //纳秒

workQueue: 一个阻塞队列,用来存储等待执行的任务,一般有以下几种选择:ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue

threadFactory:线程工厂,主要用来创建线程

handler:拒绝执行策略

ThreadPoolExecutor类的核心函数分析 任务提交过程

1.execute方法

</>复制代码

  1. public void execute(Runnable command) {
  2. if (command == null)
  3. throw new NullPointerException();
  4. /*
  5. * 进行下面三步:
  6. *
  7. * 1. 如果运行的线程小于corePoolSize,则尝试使用用户定义的Runnable对象创建一个新的线程
  8. * 调用addWorker函数会原子性的检查runState和workCount,通过返回false来防止在不应该添加
  9. * 线程时添加了线程
  10. *
  11. * 2. 如果一个任务能够成功入队列,在添加一个线程时仍需进行双重检查(因为前一次检查后该线程
  12. * 可能死亡了或进入到此方法时线程池已经shutdown了,所以需要再次检查状态);如有必要当停止时
  13. * 还需要回滚入队列操作,或当线程池没有线程时需要创建一个新线程
  14. *
  15. * 3. 如果无法入队列,那么需要增加一个新线程,如果此操作失败,那么就意味着线程池已经shutdown
  16. * 或者已经饱和了,所以拒绝任务
  17. */
  18. // 获取线程池控制状态
  19. int c = ctl.get();
  20. // worker数量小于corePoolSize
  21. if (workerCountOf(c) < corePoolSize) {
  22. // 添加worker成功则返回,不成功则再次获取线程池控制状态
  23. if (addWorker(command, true))
  24. return;
  25. c = ctl.get();
  26. }
  27. // 线程池处于RUNNING状态,将用户自定义的Runnable对象添加进Queue队列
  28. if (isRunning(c) && workQueue.offer(command)) {
  29. // 再次检查获取线程池控制状态
  30. int recheck = ctl.get();
  31. // 若此时线程池不处于RUNNING状态,将自定义任务从workQueue队列中移除
  32. if (! isRunning(recheck) && remove(command))
  33. reject(command); // 拒绝执行命令
  34. // worker数量等于0,添加worker
  35. else if (workerCountOf(recheck) == 0)
  36. addWorker(null, false);
  37. }
  38. // 添加worker失败则拒绝执行命令
  39. else if (!addWorker(command, false))
  40. reject(command);
  41. }

说明:当客户端调用submit时,之后会间接调用execute函数,其在将来某个时间执行给定任务,execute并不会直接运行给定任务,它主要调用addWorker方法
2.addWorker方法
addWorker主要是完成以下任务:

原子性增加workerCount

将用户给定的任务封装成一个worker,并将此worker添加进workers集合

启动worker对应的线程,并启动该线程运行worker的run方法

回滚worker的创建动作,即将worker从workers集合中删除并原子性的减少workerCount

</>复制代码

  1. private boolean addWorker(Runnable firstTask, boolean core) {
  2. retry:
  3. for (;;) {// 外层无限循环
  4. // 获取线程池控制状态
  5. int c = ctl.get();
  6. // 获取状态
  7. int rs = runStateOf(c);
  8. // Check if queue empty only if necessary.
  9. if (rs >= SHUTDOWN &&// 状态大于等于SHUTDOWN,初始的ctl为RUNNING,小于SHUTDOWN
  10. ! (rs == SHUTDOWN &&// 状态为SHUTDOWN
  11. firstTask == null &&// 第一个任务为null
  12. ! workQueue.isEmpty()))// worker队列不为空
  13. // 返回
  14. return false;
  15. for (;;) {
  16. // worker数量
  17. int wc = workerCountOf(c);
  18. if (wc >= CAPACITY || // worker数量大于等于最大容量
  19. wc >= (core ? corePoolSize : maximumPoolSize))// worker数量大于等于核心线程池大小或者最大线程池大小
  20. return false;
  21. if (compareAndIncrementWorkerCount(c))// 比较并增加worker的数量
  22. // 跳出外层循环
  23. break retry;
  24. // 获取线程池控制状态
  25. c = ctl.get(); // Re-read ctl
  26. if (runStateOf(c) != rs)// 此次的状态与上次获取的状态不相同
  27. // 跳过剩余部分,继续循环
  28. continue retry;
  29. // else CAS failed due to workerCount change; retry inner loop
  30. }
  31. }
  32. // worker开始标志
  33. boolean workerStarted = false;
  34. // worker被添加标志
  35. boolean workerAdded = false;
  36. Worker w = null;
  37. try {
  38. // 初始化worker
  39. w = new Worker(firstTask);
  40. // 获取worker对应的线程
  41. final Thread t = w.thread;
  42. if (t != null) {// 线程不为null
  43. // 线程池锁
  44. final ReentrantLock mainLock = this.mainLock;
  45. // 获取锁
  46. mainLock.lock();
  47. try {
  48. // Recheck while holding lock.
  49. // Back out on ThreadFactory failure or if
  50. // shut down before lock acquired.
  51. // 线程池运行状态
  52. int rs = runStateOf(ctl.get());
  53. if (rs < SHUTDOWN || // 小于SHUTDOWN
  54. (rs == SHUTDOWN && firstTask == null)) {// 等于SHUTDOWN并且firstTask为null
  55. if (t.isAlive()) // precheck that t is startable;线程刚添加进来,还未启动就存活
  56. // 抛出线程状态异常
  57. throw new IllegalThreadStateException();
  58. // worker添加到workers集合
  59. workers.add(w);
  60. // 获取集合大小
  61. int s = workers.size();
  62. if (s > largestPoolSize)// 队列大小大于largestPoolSize
  63. // 重新设置largestPoolSize
  64. largestPoolSize = s;
  65. // 设置worker已被添加标志
  66. workerAdded = true;
  67. }
  68. } finally {
  69. // 释放锁
  70. mainLock.unlock();
  71. }
  72. if (workerAdded) {// worker被添加
  73. // 开始执行worker的run方法
  74. t.start();
  75. // 设置worker已开始标志
  76. workerStarted = true;
  77. }
  78. }
  79. } finally {
  80. if (! workerStarted)// worker没有开始
  81. // 添加worker失败
  82. addWorkerFailed(w);
  83. }
  84. return workerStarted;
  85. }
任务执行过程

1、runworker方法
runWorker函数中会实际执行给定任务(即调用用户重写的run方法),并且当给定任务完成后,会继续从阻塞队列中取任务,直到阻塞队列为空(即任务全部完成).在执行给定任务时会调用钩子函数利用钩子函数可以完成用户自定义的一些逻辑,在runWorker中会调用getTask函数和processWorkerExit钩子函数

</>复制代码

  1. final void runWorker(Worker w) {
  2. // 获取当前线程
  3. Thread wt = Thread.currentThread();
  4. // 获取w的firstTask
  5. Runnable task = w.firstTask;
  6. w.firstTask = null;
  7. // 释放锁(设置state为0,允许中断)
  8. w.unlock(); // allow interrupts
  9. boolean completedAbruptly = true;
  10. try {
  11. while (task != null || (task = getTask()) != null) {
  12. // 获取锁
  13. w.lock();
  14. // If pool is stopping, ensure thread is interrupted;
  15. // if not, ensure thread is not interrupted. This
  16. // requires a recheck in second case to deal with
  17. // shutdownNow race while clearing interrupt
  18. if ((runStateAtLeast(ctl.get(), STOP) ||// 线程池运行状态至少应该高于STOP
  19. (Thread.interrupted() &&// 线程被中断
  20. runStateAtLeast(ctl.get(), STOP))) &&// 再次检查,线程池的运行状态至少应该高于STOP
  21. !wt.isInterrupted())// wt线程(当前线程)没有被中断
  22. wt.interrupt();// 中断wt线程(当前线程)
  23. try {
  24. // 在执行之前调用钩子函数
  25. beforeExecute(wt, task);
  26. Throwable thrown = null;
  27. try {
  28. // 运行给定的任务
  29. task.run();
  30. } catch (RuntimeException x) {
  31. thrown = x; throw x;
  32. } catch (Error x) {
  33. thrown = x; throw x;
  34. } catch (Throwable x) {
  35. thrown = x; throw new Error(x);
  36. } finally {
  37. // 执行完后调用钩子函数
  38. afterExecute(task, thrown);
  39. }
  40. } finally {
  41. task = null;
  42. // 增加给worker完成的任务数量
  43. w.completedTasks++;
  44. // 释放锁
  45. w.unlock();
  46. }
  47. }
  48. completedAbruptly = false;
  49. } finally {
  50. // 处理完成后,调用钩子函数
  51. processWorkerExit(w, completedAbruptly);
  52. }
  53. }

2.getTask方法
getTask函数用于从workerQueue阻塞队列中获取Runnable对象,由于是阻塞队列,所以支持有限时间等待poll和无限时间等待take.在该函数中还会相应shutdown和shutDownNow函数的操作,若检测到线程池处于SHUTDOWN或STOP状态,则会返回null,而不再返回阻塞队列中的Runnable对象

</>复制代码

  1. private Runnable getTask() {
  2. boolean timedOut = false; // Did the last poll() time out?
  3. for (;;) {// 无限循环,确保操作成功
  4. // 获取线程池控制状态
  5. int c = ctl.get();
  6. // 运行状态
  7. int rs = runStateOf(c);
  8. // Check if queue empty only if necessary.
  9. if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {// 大于等于SHUTDOWN(表示调用了shutDown)并且-->大于等于STOP(调用shutDownNow或者worker阻塞队列为空)
  10. // 减少worker数量
  11. decrementWorkerCount();
  12. // 返回null,不执行任务
  13. return null;
  14. }
  15. // 获取worker数量
  16. int wc = workerCountOf(c);
  17. // Are workers subject to culling?
  18. boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;// 是否允许coreThread超时或workerCount大于核心大小
  19. if ((wc > maximumPoolSize || (timed && timedOut))// worker数量大于maxinumPoolSize
  20. && (wc > 1 || workQueue.isEmpty())) {// workerCount大于1或worker阻塞队列为空(在阻塞队列不为空时,需要保证至少有一个wc)
  21. if (compareAndDecrementWorkerCount(c))// 比较并减少workerCount
  22. // 返回null,不执行任务,该worker会退出
  23. return null;
  24. // 跳过剩余部分,继续循环
  25. continue;
  26. }
  27. try {
  28. Runnable r = timed ?
  29. workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :// 等待指定时间
  30. workQueue.take();// 一直等待,直到有元素
  31. if (r != null)
  32. return r;
  33. // 等待指定时间后没有获取元素则超时
  34. timedOut = true;
  35. } catch (InterruptedException retry) {
  36. // 抛出了被中断异常,重试没有超时
  37. timedOut = false;
  38. }
  39. }
  40. }

3.processWorkerExit方法
processWorkerExit函数是在worker退出时调用到的钩子函数,而引起worker退出的主要因素如下:
1.阻塞队列已经为空,即没有任务可以运行了
2.调用了shutDown或shutDownNow函数
此函数会根据是否中断了空闲线程来确定是否减少workerCount的值,并且将worker从workers集合中移除并且会尝试终止线程池

</>复制代码

  1. private void processWorkerExit(Worker w, boolean completedAbruptly) {
  2. if (completedAbruptly) // 如果被中断,则需要减少workCount
  3. decrementWorkerCount();
  4. // 获取可重入锁
  5. final ReentrantLock mainLock = this.mainLock;
  6. // 获取锁
  7. mainLock.lock();
  8. try {
  9. // 将worker完成的任务添加到总的完成任务中
  10. completedTaskCount += w.completedTasks;
  11. // 从workers集合中移除该worker
  12. workers.remove(w);
  13. } finally {
  14. // 释放锁
  15. mainLock.unlock();
  16. }
  17. // 尝试终止
  18. tryTerminate();
  19. // 获取线程池控制状态
  20. int c = ctl.get();
  21. if (runStateLessThan(c, STOP)) {// 小于STOP的运行状态
  22. if (!completedAbruptly) {
  23. int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
  24. if (min == 0 && ! workQueue.isEmpty())// 允许核心超时并且workQueue阻塞队列不为空
  25. min = 1;
  26. if (workerCountOf(c) >= min)// workerCount大于等于min
  27. return; // replacement not needed
  28. }
  29. // 添加worker
  30. addWorker(null, false);
  31. }
  32. }
任务关闭过程

1.shutdown方法
shutdown会按过去执行已提交任务的顺序发起一个有序的关闭,但是不接受新任务.首先检查是否具有shutdown的权限,然后设置线程池的控制为SHUTDOWN,之后中断空闲的worker,最后尝试终止线程池.

</>复制代码

  1. public void shutdown() {
  2. final ReentrantLock mainLock = this.mainLock;
  3. mainLock.lock();
  4. try {
  5. // 检查shutdown权限
  6. checkShutdownAccess();
  7. // 设置线程控制状态为SHUTDOWN
  8. advanceRunState(SHUTDOWN);
  9. // 中断空闲worker
  10. interruptIdleWorkers();
  11. // 调用shutdown钩子函数
  12. onShutdown(); // hook for ScheduledThreadPoolExecutor
  13. } finally {
  14. mainLock.unlock();
  15. }
  16. // 尝试终止
  17. tryTerminate();
  18. }

2.tryTerminate方法

</>复制代码

  1. final void tryTerminate() {
  2. for (;;) {// 无限循环,确保操作成功
  3. // 获取线程池控制状态
  4. int c = ctl.get();
  5. if (isRunning(c) ||// 线程池的运行状态为RUNNING
  6. runStateAtLeast(c, TIDYING) ||// 线程池的运行状态最大要大于TIDYING
  7. (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))// 线程池运行状态为SHUTDOWN并且workQueue队列不为null
  8. // 不能终止,直接返回
  9. return;
  10. if (workerCountOf(c) != 0) { // 线程池正在运行的worker数量不为0
  11. // 仅仅中断一个空闲的worker
  12. interruptIdleWorkers(ONLY_ONE);
  13. return;
  14. }
  15. // 获取线程池的锁
  16. final ReentrantLock mainLock = this.mainLock;
  17. // 获取锁
  18. mainLock.lock();
  19. try {
  20. if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {// 比较并设置线程池控制状态为TIDYING
  21. try {
  22. // 终止,钩子函数
  23. terminated();
  24. } finally {
  25. ctl.set(ctlOf(TERMINATED, 0));
  26. termination.signalAll();
  27. }
  28. return;
  29. }
  30. } finally {
  31. // 释放锁
  32. mainLock.unlock();
  33. }
  34. // else retry on failed CAS
  35. }
  36. }

3.interruptIdleWorkers方法

</>复制代码

  1. private void interruptIdleWorkers(boolean onlyOne) {
  2. // 线程池的锁
  3. final ReentrantLock mainLock = this.mainLock;
  4. // 获取锁
  5. mainLock.lock();
  6. try {
  7. for (Worker w : workers) {// 遍历workers队列
  8. // worker对应的线程
  9. Thread t = w.thread;
  10. if (!t.isInterrupted() && w.tryLock()) {// 线程未被中断并且成功获得锁
  11. try {
  12. // 中断线程
  13. t.interrupt();
  14. } catch (SecurityException ignore) {
  15. } finally {
  16. // 释放锁
  17. w.unlock();
  18. }
  19. }
  20. if (onlyOne)
  21. break;
  22. }
  23. } finally {
  24. mainLock.unlock();
  25. }
  26. }

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/76812.html

相关文章

  • Java线程

    摘要:中的线程池是运用场景最多的并发框架。才是真正的线程池。存放任务的队列存放需要被线程池执行的线程队列。所以线程池的所有任务完成后,它最终会收缩到的大小。饱和策略一般情况下,线程池采用的是,表示无法处理新任务时抛出异常。 Java线程池 1. 简介 系统启动一个新线程的成本是比较高的,因为它涉及与操作系统的交互,这个时候使用线程池可以提升性能,尤其是需要创建大量声明周期很短暂的线程时。Ja...

    jerry 评论0 收藏0
  • 后端ing

    摘要:当活动线程核心线程非核心线程达到这个数值后,后续任务将会根据来进行拒绝策略处理。线程池工作原则当线程池中线程数量小于则创建线程,并处理请求。当线程池中的数量等于最大线程数时默默丢弃不能执行的新加任务,不报任何异常。 spring-cache使用记录 spring-cache的使用记录,坑点记录以及采用的解决方案 深入分析 java 线程池的实现原理 在这篇文章中,作者有条不紊的将 ja...

    roadtogeek 评论0 收藏0
  • java 四种线程的使用

    摘要:四种线程池的使用介绍的弊端及四种线程池的使用,线程池的作用线程池作用就是限制系统中执行线程的数量。相比,提供的四种线程池的好处在于重用存在的线程,减少对象创建消亡的开销,性能佳。延迟执行描述创建一个定长线程池,支持定时及周期性任务执行。 java 四种线程池的使用 介绍new Thread的弊端及Java四种线程池的使用 1,线程池的作用 线程池作用就是限制系统中执行线程的数量。 ...

    gggggggbong 评论0 收藏0
  • Java 线程

    系统启动一个线程的成本是比较高,使用线程池可以很好地提高性能,尤其是当程序中需要创建大量生存期很短暂的线程时 线程池在系统启动时即创建大量空闲线程,将一个Runnable、Callable对象—–>传给线程池—–>线程池启动里面的一个线程来执行它们的run()或者call()方法———->当线程执行体执行完成后,线程并不会死亡,而是再次返回线程池成为空闲状态,等待下一个Runnable、Calla...

    ctriptech 评论0 收藏0
  • 跟着阿里p7一起学java高并发 - 第18天:玩转java线程,这一篇就够了

    摘要:高并发系列第篇文章。简单的说,在使用了线程池之后,创建线程变成了从线程池中获取一个空闲的线程,然后使用,关闭线程变成了将线程归还到线程池。如果调用了线程池的方法,线程池会提前把核心线程都创造好,并启动线程池允许创建的最大线程数。 java高并发系列第18篇文章。 本文主要内容 什么是线程池 线程池实现原理 线程池中常见的各种队列 自定义线程创建的工厂 常见的饱和策略 自定义饱和策略 ...

    AdolphLWQ 评论0 收藏0
  • Java中的线程

    摘要:中的线程池运用场景非常广泛,几乎所有的一步或者并发执行程序都可以使用。代码中如果执行了方法,线程池会提前创建并启动所有核心线程。线程池最大数量线程池允许创建的线程最大数量。被称为是可重用固定线程数的线程池。 Java中的线程池运用场景非常广泛,几乎所有的一步或者并发执行程序都可以使用。那么线程池有什么好处呢,以及他的实现原理是怎么样的呢? 使用线程池的好处 在开发过程中,合理的使用线程...

    tomato 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<