信息发布→ 登录 注册 退出

Java线程池复用线程的秘密你知道吗

发布时间:2026-01-11

点击量:
目录
  • 前言
  • 源码探究
    • execute方法
    • addWorker方法
    • Worker类
      • 实现了Runnable接口
      • 重要属性
      • 构造方法
      • run方法
  • 执行流程
    • 总结

      前言

      我们都知道线程池可以帮我们管理线程,重复利用线程执行不同的任务。正常情况下,我们创建的线程执行完任务后就会自行销毁,那么线程池是如何做到复用线程的呢?

      源码探究

      我们从线程池ThreadPoolExecutor源码入手,一探究竟。为了突出重点,以下的方法源码过滤了部分无关代码,以求逻辑清晰。

      execute方法

      那就从线程池执行的execute方法入手吧!来看一下方法的源码

      public void execute(Runnable command) {
              if (command == null)
                  throw new NullPointerException();        
              int c = ctl.get();
              //1.小于核心线程数时,创建线程
              if (workerCountOf(c) < corePoolSize) {
                  if (addWorker(command, true))
                      return;
                  c = ctl.get();
              }
              //2.达到核心线程数,不超过队列界限时,添加到队列
              if (isRunning(c) && workQueue.offer(command)) {
                  int recheck = ctl.get();
                  if (! isRunning(recheck) && remove(command))
                      reject(command);
                  else if (workerCountOf(recheck) == 0)
                      addWorker(null, false);
              }
              //3.队列已满,不超过最大线程数时,创建线程
              else if (!addWorker(command, false))
              //4.达到最大线程数时,执行拒绝策略
                  reject(command);
          }
      

      线程池执行的4个步骤相信大家已经有所了解,这里我们只看添加线程的方法addWorker()

      addWorker方法

      private boolean addWorker(Runnable firstTask, boolean core) {
              boolean workerStarted = false;
              boolean workerAdded = false;
              Worker w = null;
              try {
              	//1.创建Worker,传入任务
                  w = new Worker(firstTask);
                  //2.取出执行任务的线程
                  final Thread t = w.thread;
                  if (t != null) {
                      final ReentrantLock mainLock = this.mainLock;
                      mainLock.lock();
                      try {                    
                          int c = ctl.get();
                          if (isRunning(c) ||
                              (runStateLessThan(c, STOP) && firstTask == null)) {
                              if (t.getState() != Thread.State.NEW)
                                  throw new IllegalThreadStateException();
                              workers.add(w);
                              workerAdded = true;
                              int s = workers.size();
                              if (s > largestPoolSize)
                                  largestPoolSize = s;
                          }
                      } finally {
                          mainLock.unlock();
                      }
                      if (workerAdded) {
                      	//3.执行线程
                          t.start();
                          workerStarted = true;
                      }
                  }
              } finally {
                  if (! workerStarted)
                      addWorkerFailed(w);
              }
              return workerStarted;
          }
      

      参数解释:

      core:true表示添加的是核心线程,false表示添加的非核心线程

      这里大家只需要关心这3行加注释的代码就可以了

      就是Worker管理了创建的线程和这个线程执行的第一个任务,并且在addWorker方法中调用线程的start方法,开启线程执行了任务。下面我们看看Worker这个类

      Worker类

      实现了Runnable接口

      Worker 是线程池ThreadPoolExecutor的内部类,实现了Runnable接口

      private final class Worker    extends AbstractQueuedSynchronizer    implements Runnable
      

      重要属性

      他有两个核心关键的属性,即封装了线程池的线程和要执行的任务,达到了线程和任务解耦的目的。

      final Thread thread;
      Runnable firstTask;
      

      构造方法

      addWorker方法会执行创建一个worker

      w = new Worker(firstTask);
      

      看一下Worker的构造方法

      Worker(Runnable firstTask) {
      	this.firstTask = firstTask;    		
      	this.thread = getThreadFactory().newThread(this);
      }
      

      可以看到 新创建的Worker本身也是一个Runnable,他的thread传的runnable任务就是worker本身

      在addWorker方法,最终会取到worker的thread属性,然后启动这个thread

      w = new Worker(firstTask);
      final Thread t = w.thread;
      ... ...
      t.start();
      

      run方法

      刚才介绍过,worker的thread的runnable参数传的就是worker本身,就是调的worker的run方法,现在我们来看最核心的worker的run方法

      public void run() { 
      	runWorker(this);
      }
      

      调的是ThreadPoolExecutor的runWorker方法

      final void runWorker(Worker w) {
          Runnable task = w.firstTask;
          w.firstTask = null;
          try { 
              while (task != null || (task = getTask()) != null) 
              {           
                  ...           
                  task.run();
                  ...
      

      可以看到runWorker核心是一个while循环,执行了第一个task之后,就不停的从队列中取任务,直到没有任务了才会执行完,销毁线程

      执行流程

      execute方法调用addWorker方法,并且执行worker.thread.start()开启线程

      ​ ——》worker.thread 执行worker本身的run方法(worker实现了Runnable接口)

      ​ ——》执行ThreadPoolExecutor的runWorker方法,是个while循环,执行完worker本身的第一个任务之后,就不停从队列取任务,直到没有任务,执行完,退出循环,销毁

      总结

      线程池将线程和任务进行解耦,线程是线程,任务是任务,摆脱了之前通过 Thread 创建线程时的一个线程必须对应一个任务的限制。

      在线程池中,同一个线程可以从阻塞队列中不断获取新任务来执行,其核心原理在于线程池对 Thread 进行了封装,并不是每次执行任务都会调用 Thread.start() 来创建新线程,而是让每个线程去执行一个“循环任务”,在这个“循环任务”中不停的检查是否有任务需要被执行,如果有则直接执行,也就是调用任务中的 run 方法,将 run 方法当成一个普通的方法执行,通过这种方式将只使用固定的线程就将所有任务的 run 方法串联起来。

      本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注的更多内容!             

      在线客服
      服务热线

      服务热线

      4008888355

      微信咨询
      二维码
      返回顶部
      ×二维码

      截屏,微信识别二维码

      打开微信

      微信号已复制,请打开微信添加咨询详情!