栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

线程池线程复用的原理

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

线程池线程复用的原理

前言

线程池最大的作用就是复用线程。在线程池中,经过同一个线程去执行不一样的任务,减少反复地创建线程带来的系统开销,就是线程的复用。那么线程池线程复用的原理是什么呢?

之前面试被问到线程池复用的原理时,由于对源码不甚了解,回答的不好。因此这篇文章将深入源码,理解线程复用到底时如何实现的。

一、线程池核心属性

首先我们看看线程池的核心属性,这也是面试中经常被问到的问题。

public class ThreadPoolExecutor extends AbstractExecutorService {

 //线程状态,高3为表示线程池状态,低29位表示线程数量
 private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));

 //Lock锁
 private final ReentrantLock mainLock = new ReentrantLock();
 //条件变量
 private final Condition termination = mainLock.newCondition();
 //线程集合
 private final HashSet workers = new HashSet<>();
 
 //核心线程数
 private volatile int corePoolSize;
 //最大线程数
 private volatile int maximumPoolSize;
 //阻塞队列
 private final BlockingQueue workQueue;
 //非核心线程存活时间
 private volatile long keepAliveTime;
 //线程工厂,所有线程使用线程工厂创建
 private volatile ThreadFactory threadFactory;
 //拒绝策略,是一个函数式接口
 private volatile RejectedExecutionHandler handler;

}

二、execute源码

我们可以从execute() 方法开始,查看线程复用的原理

private static final int COUNT_BITS = Integer.SIZE - 3; //32-3
private static final int COUNT_MASK = (1 << COUNT_BITS) - 1; 2^29-1

//c & COUNT_MASK; 返回低29位,也就是线程树
private static int workerCountOf(int c) { return c & COUNT_MASK; } 

public void execute(Runnable command) {
  if (command == null)
   throw new NullPointerException();
  
  int c = ctl.get();
  
  //得到线程数,判断<核心线程数
  if (workerCountOf(c) < corePoolSize) {
   //在线程池中建立一个线程并执行传入的任务,成功返回true,否则返回false
   if (addWorker(command, true))
    return;
   c = ctl.get();
  }
  //如果线程池还是Running状态且核心线程已满,此时以核心线程数为阈值,将command加入阻塞队列
  if (isRunning(c) && workQueue.offer(command)) {
   int recheck = ctl.get();
   //如果线程池不是Running状态,且移除成功
   if (! isRunning(recheck) && remove(command))
    // 若是线程池处于非运行状态,而且把当前的任务从任务队列中移除成功,则拒绝该任务
    reject(command);
   //若线程数为0
   else if (workerCountOf(recheck) == 0)
    // 若是以前的线程已经被销毁完,新建一个非核心线程
    addWorker(null, false);
  }
  //如果队列已满,再一次尝试执行此任务,此时以最大线程数为阈值,失败则执行拒绝策略
  else if (!addWorker(command, false))
   reject(command);
}



以上的步骤可以以流程图来表示

三、addWorker源码

addWorker 方法的主要做用是在线程池中建立一个线程并执行传入的任务,若是返回 true 表明添加成功,若是返回 false 表明添加失败。

private boolean addWorker(Runnable firstTask, boolean core) {
  retry:
  for (int c = ctl.get();;) {
   // Check if queue empty only if necessary.
   if (runStateAtLeast(c, SHUTDOWN)
    && (runStateAtLeast(c, STOP)
     || firstTask != null
     || workQueue.isEmpty()))
    return false;

   for (;;) {
    if (workerCountOf(c)
     >= ((core ? corePoolSize : maximumPoolSize) & COUNT_MASK))
     return false;
    if (compareAndIncrementWorkerCount(c))
     break retry;
    c = ctl.get(); // Re-read ctl
    if (runStateAtLeast(c, SHUTDOWN))
     continue retry;
    // else CAS failed due to workerCount change; retry inner loop
   }
  }

  boolean workerStarted = false;
  boolean workerAdded = false;
  Worker w = null;
  try {
   //创建新线程w
   w = new Worker(firstTask);
   final Thread t = w.thread;
   if (t != null) {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock(); //加锁
    try {
     // Recheck while holding lock.
     // Back out on ThreadFactory failure or if
     // shut down before lock acquired.
     int c = ctl.get();

     if (isRunning(c) ||
      (runStateLessThan(c, STOP) && firstTask == null)) {
      if (t.getState() != Thread.State.NEW)
       throw new IllegalThreadStateException();
      //将新建线程w加入集合
      workers.add(w);
      workerAdded = true;
      int s = workers.size();
      if (s > largestPoolSize)
       largestPoolSize = s;
     }
    } finally {
     mainLock.unlock();
    }
    if (workerAdded) {
     //运行线程t
     t.start();
     workerStarted = true;
    }
   }
  } finally {
   if (! workerStarted)
    addWorkerFailed(w);
  }
  return workerStarted;
 }

现在,我们已经创建了一个线程来执行任务,我们发现,当线程数小于核心线程数或者当队列已经满时且线程数小于最大线程数时,addWorker会创建一个新线程来执行新任务,那么当阻塞队列未满时,怎么复用核心线程呢?答案就在29行,Worker类中。

四、Worker类源码
 private final class Worker
  extends AbstractQueuedSynchronizer
  implements Runnable
 {
  
  private static final long serialVersionUID = 6138294804551838833L;

  
  @SuppressWarnings("serial") // Unlikely to be serializable
  final Thread thread;
  
  @SuppressWarnings("serial") // Not statically typed as Serializable
  Runnable firstTask;
  
  volatile long completedTasks;

  // TODO: switch to AbstractQueuedLongSynchronizer and move
  // completedTasks into the lock word.

  
  Worker(Runnable firstTask) {
   setState(-1); // inhibit interrupts until runWorker
   this.firstTask = firstTask;
   this.thread = getThreadFactory().newThread(this); //使用线程工厂创建线程
  }

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

  // Lock methods
  //
  // The value 0 represents the unlocked state.
  // The value 1 represents the locked state.

  protected boolean isHeldExclusively() {
   return getState() != 0;
  }

  ......
 }

Worker实现了Runnable接口,并且有一个final属性thread,当addWorker()方法执行new Worker(firstTask)时,构造器通过ThreadFactory 创建新线程,并将当前Runnable传入newThread()。因此,addWorker() 方法执行t.start后,当该线程抢到cpu执行权时,将执行runWorker(this);

final void runWorker(Worker w) {
  Thread wt = Thread.currentThread();
  //取出w的firstTask
  Runnable task = w.firstTask;
  //将w的firstTask置为null
  w.firstTask = null;
  w.unlock(); // allow interrupts
  boolean completedAbruptly = true;
  try {
   //线程第一次创建后,task不为null;当执行完之后的线程之后,该线程会从阻塞队列中去取任务
   while (task != null || (task = getTask()) != null) {
    w.lock();
    // If pool is stopping, ensure thread is interrupted;
    // if not, ensure thread is not interrupted. This
    // requires a recheck in second case to deal with
    // shutdownNow race while clearing interrupt
    if ((runStateAtLeast(ctl.get(), STOP) ||
      (Thread.interrupted() &&
      runStateAtLeast(ctl.get(), STOP))) &&
     !wt.isInterrupted())
     wt.interrupt();
    try {
     //该方法的方法体为null,留给我们自己扩展
     beforeExecute(wt, task);
     try {
      //直接调用task的run方法
      task.run();
      //该方法的方法体为null,留给我们自己扩展
      afterExecute(task, null);
     } catch (Throwable ex) {
      afterExecute(task, ex);
      throw ex;
     }
    } finally {
     task = null;
     w.completedTasks++;
     w.unlock();
    }
   }
   completedAbruptly = false;
  } finally {
   processWorkerExit(w, completedAbruptly);
  }
 }

在这里,我们终于找到了线程复用的最终实现,当调用execute方法或者submit方法时,会判断当前线程数是不是小于核心线程数,或者当线程数大于核心线程数且小于最大线程数且阻塞队列未满,线程池会在addWorker方法中新建一个Worker线程,并将该线程添加进线程集合中,线程状态ctl加1,然后运行该线程。

在Worker的run方法中会调用runWorker方法,该方法通过一个循环进行线程的复用,while (task != null || (task = getTask()) != null)。当task≠null(当线程执行创建线程时的任务时)或者(task = getTask()) != null(从阻塞队列中取任务赋值给task)时,直接运行task.run方法而不是新建线程。也就是说,每一个线程都始终在一个大循环中,反复获取任务,而后执行任务,从而实现了线程的复用。

 


小伙伴们有兴趣想了解内容和更多相关学习资料的请点赞收藏+评论转发+关注我,后面会有很多干货。
我有一些面试题、架构、设计类资料可以说是程序员面试必备!所有资料都整理到网盘了,需要的话欢迎下载!私信我回复【07】即可免费获取

 

原文出处:www.shaoqun.com/a/1798524.html

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/943951.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号