• 手写一个线程池


    自己手动写一个线程池的必要条件需要先了解我们使用的线程池的功能。为什么会有线程池?这是为了减少线程创建和销毁的开销。复用线程的目的。为了达到这个目的。预计方案是:需要一个存放任务的队列,主线程相当于生产者,在这个队列里面push任务,启动几个核心线程一直消费这个队列,然后执行其中的任务。

    1、线程池的几个重要参数

    (1)corePoolSize 核心线程数 含义是一直存活的线程数

    (2)maxPoolSize 最大线程数 含义是最大运行的线程个数

    (3)keepaliveTime 存活的时间 超过核心线程数的线程存活时间

    (4)unit 单位

    (5)blockingQueue 阻塞队列 放置任务使用

    (6)threadFactory 线程工厂

    (7)拒绝策略:abort策略表示拒绝任务,但是会抛异常,discard策略表示拒绝任务,不会抛异常。CallerRunPolicy策略表示会使用调用者去执行任务。DiscardoldPolicy策略表示抛弃老的任务,将新的任务添加进队列

    上述几种参数决定了线程池要对外暴露什么样的接口,精选1-5必要参数手动写一个线程池。

    2、代码逻辑

    我这边编写的步骤是:

    1、先将参数定义好,构造方法定义出来,除了上述必要的1-5的参数外,还需要一个消费者工作集合。为什么要保存成集合呢?主要用于在取消的时候遍历其集合,然后进行中断。

    2、编写submit方法逻辑,首先参数肯定是Runable任务,判断逻辑:

    (1)当运行的工作线程小于核心线程,则直接启动一个工作线程然后加入线程集合中。

    (2)当1不成立后,那就需要加入阻塞队列等待了,如果阻塞队列是数组类型阻塞队列,这时有范围限制,如果不超过,则加入队列。

    (3)当阻塞队列超了,则查看当前线程个数是否小于最大线程个数。如果不超过,则启动一个工作线程消费其任务,并且加入其工作集合中

    (4)如果超过最大线程数,实际上走拒绝策略逻辑,这里我们就直接报错。

    3、上述工作线程内部怎么消费呢?因为是阻塞队列,自然就是循环队列中取任务然后执行。而这里的取任务,分成了poll和take。这是为什么?从上面步骤可知核心线程数超过之后也会存在启动工作线程的情况,那这些线程有一个保活时间,时间一到,只要此线程空闲,线程则会跳出循环结束执行。而这里阻塞队列的poll可以实现上述逻辑。take则在没有任务时就阻塞。

    下面是按照其上述逻辑的代码,大家可以参考。

    1. package org.example.Thread1;
    2. import java.util.HashSet;
    3. import java.util.concurrent.BlockingQueue;
    4. import java.util.concurrent.TimeUnit;
    5. import java.util.concurrent.atomic.AtomicInteger;
    6. import java.util.concurrent.locks.ReentrantLock;
    7. public class SelfThreadPoolExecutor {
    8. //一直存活的核心线程数
    9. private int corePoolSize;
    10. //运行存在的最大的线程数
    11. private int maxPoolSize;
    12. private AtomicInteger status = new AtomicInteger();
    13. private AtomicInteger workCount = new AtomicInteger();
    14. private final static Integer RUNNING = 0;
    15. private final static Integer STOP = 1;
    16. private int keepAliveTime;
    17. private BlockingQueue blockingQueue;
    18. private HashSet hashSet = new HashSet<>();
    19. private ReentrantLock mainLock = new ReentrantLock();
    20. public SelfThreadPoolExecutor(int corePoolSize, int maxPoolSize, int keepAliveTime, BlockingQueue blockingQueue) {
    21. this.corePoolSize = corePoolSize;
    22. this.maxPoolSize = maxPoolSize;
    23. this.keepAliveTime = keepAliveTime;
    24. this.blockingQueue = blockingQueue;
    25. }
    26. public void submit(Runnable runnable) {
    27. if (status.get() == STOP) {
    28. throw new RuntimeException("不能添加新的任务了");
    29. }
    30. if (workCount.get() < corePoolSize && addWork(runnable, true)) {
    31. return;
    32. }
    33. if (blockingQueue.offer(runnable)) {
    34. return;
    35. }
    36. if (workCount.get() < maxPoolSize && addWork(runnable, false)) {
    37. return;
    38. }
    39. throw new RuntimeException("拒绝此任务");
    40. }
    41. public boolean addWork(Runnable runnable, boolean core) {
    42. if (status.get() == STOP) {
    43. return false;
    44. }
    45. while (true) {
    46. if (workCount.get() > (core ? corePoolSize : maxPoolSize)) {
    47. return false;
    48. }
    49. boolean inc = workCount.compareAndSet(workCount.get(), workCount.get()+1);
    50. if (!inc) {
    51. continue;
    52. }
    53. break;
    54. }
    55. mainLock.lock();
    56. try {
    57. Worker worker = new Worker(runnable);
    58. worker.thread.start();
    59. hashSet.add(worker);
    60. } finally {
    61. while (true) {
    62. boolean inc = workCount.compareAndSet(workCount.get(), workCount.get()-1);
    63. if (!inc) {
    64. continue;
    65. }
    66. break;
    67. }
    68. mainLock.unlock();
    69. }
    70. return true;
    71. }
    72. public void shutdown() {
    73. mainLock.lock();
    74. try {
    75. while (true) {
    76. if (status.get() == STOP) {
    77. break;
    78. }
    79. boolean stop = status.compareAndSet(status.get(), STOP);
    80. if (stop) {
    81. break;
    82. }
    83. }
    84. for (Worker worker : hashSet) {
    85. if (!worker.thread.isInterrupted()) {
    86. worker.thread.interrupt();
    87. }
    88. }
    89. }finally {
    90. mainLock.unlock();
    91. }
    92. }
    93. private final class Worker implements Runnable{
    94. Runnable firstTask;
    95. final Thread thread;
    96. public Worker(Runnable firstTask) {
    97. this.firstTask = firstTask;
    98. thread = new Thread(this);
    99. }
    100. @Override
    101. public void run() {
    102. runWork(this);
    103. }
    104. public void runWork(Worker w) {
    105. Thread wt = w.thread;
    106. Runnable task = w.firstTask;
    107. w.firstTask = null;
    108. try {
    109. while (task != null || (task = getTask()) != null) {
    110. try {
    111. if (wt.isInterrupted()) {
    112. break;
    113. }
    114. if (status.get() == STOP) {
    115. break;
    116. }
    117. task.run();
    118. } finally {
    119. task = null;
    120. }
    121. }
    122. } finally {
    123. mainLock.lock();
    124. try {
    125. hashSet.remove(w);
    126. } finally {
    127. mainLock.unlock();
    128. }
    129. }
    130. }
    131. public Runnable getTask() {
    132. boolean timedOut = false;
    133. while (true) {
    134. try {
    135. if (status.get() == STOP) {
    136. return null;
    137. }
    138. if (timedOut) {
    139. return null;
    140. }
    141. Runnable task = null;
    142. if (workCount.get() > corePoolSize) {
    143. task = blockingQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS);
    144. } else {
    145. task = blockingQueue.take();
    146. }
    147. if (task != null) {
    148. return task;
    149. }
    150. timedOut = true;
    151. } catch (InterruptedException e) {
    152. timedOut = true;
    153. }
    154. }
    155. }
    156. }
    157. }

     

     

  • 相关阅读:
    【OpenCV 例程 300篇】245. 特征检测之 BRISK 算子
    设计模式胡咧咧之策略工厂实现导入导出
    基于JAVA社区便民服务系统社区便民服务计算机毕业设计源码+系统+mysql数据库+lw文档+部署
    架构师之路5. 浪潮LG - 离职
    架构设计 - 本地热点缓存
    【dgl学习】dgl.canonical_etypes函数解析
    Python计算机毕业设计基于Django的学生作业管理系统
    1600*C. Binary String Copying
    Opencv3.4版本+ffmpeg联合编译
    【直播笔记0628】 高频面试并发的本质:JAVA程序员应该掌握的并发知识
  • 原文地址:https://blog.csdn.net/m0_61522454/article/details/136610438