从数据结构开始,了解线程池的设计

50 阅读2分钟

从数据结构开始,了解线程池的设计

  1. ctl 《AtomicInteger》 线程池核心控制字段 image.png

  2. mainLock 《ReenTtranLock》:当执行HashSet《Worker》的插入、移除等操作时,由于他不是线程安全的,所以需要mainLock加锁

  3. BlockingQueue《Runnable》任务队列,worker会从blockingQueue中不断获取任务,然后执行

  4. workers = HashSet《Worker》,worker表示线程池的基本工作单元

  5. Worker,线程池基本工作单元

    • 实现与继承
      • 继承《extends》 AQS

        • 编写lock方法,当worker执行lock方法时,表明当前的worker正在执行task任务,task任务的来源为blockingQueue
        • 编写unLock方法,执行完unLock方法,worker进入空闲状态

        当线程池执行stop方法时,会循环hashSet中所有的worker,获取他们是否处于加锁状态,直到所有的worker都处于空闲状态时,才会最终终止线程池

      • 实现《implements》Runnable,实现了run方法

    • 类结构
      • firstTask《Runnable》创建Worker时,即线程池执行execute方法时,本身会传入一个task
      • thread,工作线程,用于执行runnable任务

执行逻辑

说明:执行逻辑准备分三个部分进行介绍,一部分是主流程,即ThreadPoolExecutor的execute方法;第二部分是worker执行流程;

  1. ThreadPoolExecutor#execute
    • 核心线程数、最大线程数,表示的都是HashSet《Worker》中的worker的数量,用ctl的低28位记数

    • 在执行execute方法时,指定入参task《Runnable》;如下图,当需要增加Worker时,需要创建Worker对象,过程如下:

      • 重要:::通过内部的TreadFactory创建线程,需要指定一个Runnable,前面我说过,Worker实现了Runnable,所以此处创建的线程,指定的runnable是worker本身
          Worker(Runnable firstTask) {
              setState(-1); // inhibit interrupts until runWorker
              this.firstTask = firstTask;
              this.thread = getThreadFactory().newThread(this);
          }
      
      • 使用mainLock加锁,然后将worker添加到HashSet《Worker》中
      • 添加完成后,执行 worker.thread.start(),启动线程
      • 线程启动后,会自动执行worker的run方法,具体内容后面会继续介绍
      • mainLock解锁

image.png

  1. Worker的run方法执行过程,核心的执行逻辑
    • run方法内部,是一个while(true)的轮询,不断的从blockingQueue中获取task,执行。
    • aqs的作用:worker加锁,表明worker这个工作单元正在执行任务,解锁后,表示处于空闲状态

image.png