线程池线程复用原理(源码详解)

1.什么是线程复用?

在线程池中,同一个线程去执行不同的任务,这就是线程复用。

假设有50个任务,线程池设置核心线程数为3,等待队列数设置为5,那么执行这50个任务时,这3个核心线程和2个非核心线程就会不停的复用,进行任务的执行。

2.线程复用原理解析

2.1线程池的工作流程
在这里插入图片描述
当任务提交之后,线程池首先会检查当前线程数,如果当前的线程数小于核心线程数(corePoolSize),则新建线程并执行任务。

当提交的任务不断增加,创建的线程数等于核心线程数(corePoolSize),新增的任务会被添加到 workQueue 任务队列中,等待核心线程执行完当前任务后,重新从 workQueue 中获取任务执行。

当任务数量达到了 workQueue 的最大容量,但是当前线程数小于最大线程数(maximumPoolSize),线程池会在核心线程数(corePoolSize)的基础上继续创非核心建线程来执行任务。

当任务继续增加,线程池的线程数达到最大线程数(maximumPoolSize),这个时候线程池就会采用拒绝策略来拒绝这些任务。

2.2源码解析

public void execute(Runnable command) {
     // 如果传入的Runnable的空,就抛出异常
     if (command == null)
         throw new NullPointerException();
     int c = ctl.get();
     // 线程池中的线程比核心线程数少 
     if (workerCountOf(c) < corePoolSize) {
         // 新建一个核心线程执行任务
         if (addWorker(command, true))
             return;
         c = ctl.get();
     }
     // 核心线程已满,但是任务队列未满,添加到队列中
     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);
     }
     // 核心线程池已满,队列已满,尝试创建一个非核心新的线程
     else if (!addWorker(command, false))
         // 如果创建新线程失败,说明线程池关闭或者线程池满了,拒绝任务
         reject(command);
 }

判断当前线程数是否小于核心线程数,如果小于核心线程数就调用 addWorker() 方法增加一个 Worker,这里的 Worker 就可以理解为一个线程。addWorker 方法的主要作用是在线程池中创建一个线程并执行传入的任务,如果返回 true 代表添加成功,如果返回 false 代表添加失败。

2.3线程复用源码解析

    final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        w.unlock(); // 释放锁 设置work的state=0 允许中断
        boolean completedAbruptly = true;
        try {
            //一直执行 如果task不为空 或者 从队列中获取的task不为空
            while (task != null || (task = getTask()) != null) {
                    task.run();//执行task中的run方法
                }
            }
            completedAbruptly = false;
        } finally {
            //1.将 worker 从数组 workers 里删除掉
            //2.根据布尔值 allowCoreThreadTimeOut 来决定是否补充新的 Worker 进数组 workers
            processWorkerExit(w, completedAbruptly);
        }
    }

到这里已经很明确了,线程的复用是通过while循环实现的,worker会首先获取当前的firstTask进行run,然后不停的循环从等待队列中获取新的任务(task),如果有新任务则直接调用task的run方法,不会再去新建一个线程,从而实现复用。

下面附上getTask()的部分源码

      try {
            //从队列中获取等待中的任务
            Runnable r = timed ?
            workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
            workQueue.take();
            if (r != null)
                return r;
            timedOut = true;
      } catch (InterruptedException retry) {
            timedOut = false;
      }

3.小结

线程池中的worker通过获取本身firstTask 或者 通过getTask方法 从等待队列中不停的循环去获取新的task,调用task的run方法执行任务,来实现线程的复用。

猜你喜欢

转载自blog.csdn.net/huyaowei789/article/details/107101414