JUC线程池(3):线程池源码分析

上一章介绍了线程池的数据结构,这一章将会通过源码进行分析

线程池实例

在分析源码之前,我们先来使用一下:

package com.test;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Test {
    
    
    
    public static void main(String[] args) {
        //创建一个可重用固定线程数的线程池
        ExecutorService pool = Executors.newFixedThreadPool(2);
        
        Runnable runnable = new Runnable() {

            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+ " is running.");
            }
        };

        Runnable runnable1 = new Runnable() {

            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+ " is running.");
            }
        };

        pool.execute(runnable);
        pool.execute(runnable1);
        
        pool.shutdown();
    }
}

在示例中,包括了线程池的创建,将任务添加到线程池,关闭线程池这三个主要的步骤。我们会从这3个步骤来分析ThreadPoolExecutor。

参考代码(基于jdk1.8)

详细内容可以去看看Executors和ThreadPoolExecutor完整源码

线程池源码分析

(一)创建“线程池”

下面以newFixedThreadPool()介绍线程池的创建过程。

1.newFixedThreadPool()

newFixedThreadPool()在Executors.java中定义,源码如下:

    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

说明

  • newFixedThreadPool(int nThreads)的作用是创建一个线程池,线程池的容量是nThreads。
  • newFixedThreadPool()在调用ThreadPoolExecutor()时,会传递一个LinkedBlockingQueue()对象,而LinkedBlockingQueue是单向链表实现的阻塞队列。在线程池中,就是通过该阻塞队列来实现“当线程池中任务数量超过运行的任务数量时,部分任务会阻塞等待”。
2.ThreadPoolExecutor()

ThreadPoolExecutor()在ThreadPoolExecutor.java中定义,源码如下:

   public ThreadPoolExecutor(int corePoolSize,
                             int maximumPoolSize,
                             long keepAliveTime,
                             TimeUnit unit,
                             BlockingQueue<Runnable> workQueue) {
       this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
            Executors.defaultThreadFactory(), defaultHandler);
   }

说明:该函数实际上调用ThreadPoolExecutor的另外一个构造函数:

   public ThreadPoolExecutor(int corePoolSize,
                             int maximumPoolSize,
                             long keepAliveTime,
                             TimeUnit unit,
                             BlockingQueue<Runnable> workQueue,
                             ThreadFactory threadFactory,
                             RejectedExecutionHandler handler) {
       if (corePoolSize < 0 ||
           maximumPoolSize <= 0 ||
           maximumPoolSize < corePoolSize ||
           keepAliveTime < 0)
           throw new IllegalArgumentException();
       if (workQueue == null || threadFactory == null || handler == null)
           throw new NullPointerException();
        // 核心池大小
       this.corePoolSize = corePoolSize;
       //最大池大小
       this.maximumPoolSize = maximumPoolSize;
       //线程等待队列
       this.workQueue = workQueue;
       this.keepAliveTime = unit.toNanos(keepAliveTime);
       //线程工场对象
       this.threadFactory = threadFactory;
       //拒绝策略的句柄
       this.handler = handler;
   }

说明:在ThreadPoolExecutor()的构造函数中,进行的是初始化工作。
corePoolSize, maximumPoolSize, unit, keepAliveTime和workQueue这些变量的值是已知的,它们都是通过newFixedThreadPool()传递而来。下面看看threadFactory和handler对象,它们可以手动传递过来,或者使用系统默认的。

2.1ThreadFactory

线程池中的ThreadFactory是一个线程工厂,线程池创建线程都是通过线程工场对象(threadFactory)来完成的。

上面的threadFactory对象,是通过Executors.defaultThreadFactory()返回的。Executors.java中的defaultThreadFactory()源码如下:

   public static ThreadFactory defaultThreadFactory() {
       return new DefaultThreadFactory();
   }

defaultThreadFactory()返回DefaultThreadFactory对象。Executors.java中的DefaultThreadFactory()源码如下:

   static class DefaultThreadFactory implements ThreadFactory {
       private static final AtomicInteger poolNumber = new AtomicInteger(1);
       private final ThreadGroup group;
       private final AtomicInteger threadNumber = new AtomicInteger(1);
       private final String namePrefix;

       DefaultThreadFactory() {
           SecurityManager s = System.getSecurityManager();
           group = (s != null) ? s.getThreadGroup() :
                                 Thread.currentThread().getThreadGroup();
           namePrefix = "pool-" +
                         poolNumber.getAndIncrement() +
                        "-thread-";
       }

       // 提供创建线程的API。
       public Thread newThread(Runnable r) {
           // 线程对应的任务是Runnable对象r
           Thread t = new Thread(group, r,
                                 namePrefix + threadNumber.getAndIncrement(),
                                 0);
           //设为“非守护线程”
           if (t.isDaemon())
               t.setDaemon(false);
           //将优先级设为“Thread.NORM_PRIORITY”
           if (t.getPriority() != Thread.NORM_PRIORITY)
               t.setPriority(Thread.NORM_PRIORITY);
           return t;
       }
   }

说明:ThreadFactory的作用就是提供创建线程的功能的线程工厂。它是通过newThread()提供创建线程功能的,下面简单说说newThread()。newThread()创建的线程对应的任务是Runnable对象,它创建的线程都是“非守护线程”而且“线程优先级都是Thread.NORM_PRIORITY”。

2.2RejectedExecutionHandler

handler是ThreadPoolExecutor中拒绝策略的处理句柄。所谓拒绝策略,是指将任务添加到线程池中时,线程池拒绝该任务所采取的相应策略。

线程池默认会采用的是defaultHandler策略,即AbortPolicy策略。在AbortPolicy策略中,线程池拒绝任务时会抛出异常!
defaultHandler的定义如下:

   private static final RejectedExecutionHandler defaultHandler =
       new AbortPolicy();

AbortPolicy的源码如下:

    public static class AbortPolicy implements RejectedExecutionHandler {
       /**
        * Creates an {@code AbortPolicy}.
        */
       public AbortPolicy() { }

       /**
        * Always throws RejectedExecutionException.
        *
        * @param r the runnable task requested to be executed
        * @param e the executor attempting to execute this task
        * @throws RejectedExecutionException always.
        */
       public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
           //抛异常
           throw new RejectedExecutionException("Task " + r.toString() +
                                                " rejected from " +
                                                e.toString());
       }
   }

(二)添加任务到“线程池”

1.execute()

execute()定义在ThreadPoolExecutor.java中,源码如下:

public void execute(Runnable command) {
       // 如果任务为null,则抛出异常。
       if (command == null)
           throw new NullPointerException();
        // 获取ctl对应的int值。该int值保存了"线程池中任务的数量"和"线程池状态"信息
       int c = ctl.get();
       // 当线程池中的任务数量 < "核心池大小"时,即线程池中少于corePoolSize个任务。
      // 则通过addWorker(command, true)新建一个线程,并将任务(command)添加到该线程中;然后,启动该线程从而执行任务。
       if (workerCountOf(c) < corePoolSize) {
           if (addWorker(command, true))
               return;
           c = ctl.get();
       }
       // 当线程池中的任务数量 >= "核心池大小"时,
       // 而且,"线程池处于允许状态"时,则尝试将任务添加到阻塞队列中。
       if (isRunning(c) && workQueue.offer(command)) {
           int recheck = ctl.get();
           // 再次确认“线程池状态”,若线程池异常终止了,则删除任务;然后通过reject()执行相应的拒绝策略的内容。
           if (! isRunning(recheck) && remove(command))
               reject(command);
           // 否则,如果"线程池中任务数量"为0,则通过addWorker(null, false)尝试新建一个线程,新建线程对应的任务为null。
           else if (workerCountOf(recheck) == 0)
               addWorker(null, false);
       }
       // 通过addWorker(command, false)新建一个线程,并将任务(command)添加到该线程中;然后,启动该线程从而执行任务。
       // 如果addWorker(command, false)执行失败,则通过reject()执行相应的拒绝策略的内容。
       else if (!addWorker(command, false))
           reject(command);
   }

说明:execute()的作用是将任务添加到线程池中执行,它会分为3种情况进行处理:

  • 1.如果“线程池中任务数量” < “核心池大小”,即线程池中少于corePoolSize个任务;此时就新建一个线程,并将该任务添加到线程中进行进行。
  • 2.如果"线程池中任务数量" >= "核心池大小",并且"线程池是允许状态";此时,则将任务添加到阻塞队列中阻塞等待。在该情况下,会再次确认"线程池的状态",如果"第2次读到的线程池状态"和"第1次读到的线程池状态"不同,则从阻塞队列中删除该任务。
  • 3.非以上两种情况。在这种情况下,尝试新建一个线程,并将该任务添加到线程中进行执行。如果执行失败,则通过reject()拒绝该任务。
2.addWorker()

addWorker()的源码如下:

   private boolean addWorker(Runnable firstTask, boolean core) {
       retry:
       //更新“线程池状态和计数”标记,即更新ctl
       for (;;) {
           //获取ctl对应的int值,该int值保证了“线程池中任务的数量”和“线程池状态”信息
           int c = ctl.get();
           //获取线程池状态
           int rs = runStateOf(c);

           // 有效性检查
           if (rs >= SHUTDOWN &&
               ! (rs == SHUTDOWN &&
                  firstTask == null &&
                  ! workQueue.isEmpty()))
               return false;

           for (;;) {
               //获取线程池中任务的数量
               int wc = workerCountOf(c);
               //如果“线程池中任务的数量”超过限制,则返回false
               if (wc >= CAPACITY ||
                   wc >= (core ? corePoolSize : maximumPoolSize))
                   return false;
               //通过CAS将c的值+1,如果操作失败,则退出循环
               if (compareAndIncrementWorkerCount(c))
                   break retry;
               c = ctl.get();  // Re-read ctl
               // 检查"线程池状态",如果与之前的状态不同,则从retry重新开始。
               if (runStateOf(c) != rs)
                   continue retry;
               // else CAS failed due to workerCount change; retry inner loop
           }
       }

       boolean workerStarted = false;
       boolean workerAdded = false;
       Worker w = null;
       // 添加任务到线程池,并启动任务所在的线程。
       try {
           final ReentrantLock mainLock = this.mainLock;
          // 新建Worker,并且指定firstTask为Worker的第一个任务。
           w = new Worker(firstTask);
           // 获取Worker对应的线程
           final Thread t = w.thread;
           if (t != null) {
               //获取锁
               mainLock.lock();
               try {
                   // Recheck while holding lock.
                   // Back out on ThreadFactory failure or if
                   // shut down before lock acquired.
                   int c = ctl.get();
                   int rs = runStateOf(c);

                   // 再次确认"线程池状态"
                   if (rs < SHUTDOWN ||
                       (rs == SHUTDOWN && firstTask == null)) {
                       if (t.isAlive()) // precheck that t is startable
                           throw new IllegalThreadStateException();
                       // 将Worker对象(w)添加到"线程池的Worker集合(workers)"中
                       workers.add(w);
                       int s = workers.size();
                       // 更新largestPoolSize
                       if (s > largestPoolSize)
                           largestPoolSize = s;
                       workerAdded = true;
                   }
               } finally {
                   // 释放锁
                   mainLock.unlock();
               }
               // 如果"成功将任务添加到线程池"中,则启动任务所在的线程。 
               if (workerAdded) {
                   t.start();
                   workerStarted = true;
               }
           }
       } finally {
           if (! workerStarted)
               addWorkerFailed(w);
       }
       // 返回任务是否启动。
       return workerStarted;
   }

说明:

  • addWorker(Runnable firstTask, boolean core)的作用是将任务(firstTask)添加到线程池中,并启动该任务。
    core为true的话,则以corePoolSize为界限,若“线程池中已有任务数量 >= corePoolSize”,则返回false;core为false的话,则以maximumPoolSize为界限,若"线程池中已有任务数量>=maximumPoolSize",则返回false。
  • addWorker()会先通过for循环不断尝试更新ctl状态,ctl记录了"线程池中任务数量和线程池状态"。更新成功之后,再通过try模块来将任务添加到线程池中,并启动任务所在的线程。
    从addWorker()中,我们能清晰的发现:线程池在添加任务时,会创建任务对应的Worker对象;而一个Workder对象包含一个Thread对象。(01) 通过将Worker对象添加到"线程的workers集合"中,从而实现将任务添加到线程池中。 (02) 通过启动Worker对应的Thread线程,则执行该任务。
3.submit()

还有,AbstractExecutorService的submit()方法,也就是ThreadPoolExecutor的submit()方法(ThreadPoolExecutor继承AbstractExecutorService)也是通过调用execute()方法实现的,源码如下:

   public Future<?> submit(Runnable task) {
       if (task == null) throw new NullPointerException();
       RunnableFuture<Void> ftask = newTaskFor(task, null);
       execute(ftask);
       return ftask;
   }

(三)关闭“线程池”

shutdown()源码如下:

   public void shutdown() {
       final ReentrantLock mainLock = this.mainLock;
       // 获取锁
       mainLock.lock();
       try {
           // 检查终止线程池的“线程”是否有权限。
           checkShutdownAccess();
           // 设置线程池的状态为关闭状态。
           advanceRunState(SHUTDOWN);
           // 中断线程池中空闲的线程。
           interruptIdleWorkers();
           // 钩子函数,在ThreadPoolExecutor中没有任何动作。
           onShutdown(); // hook for ScheduledThreadPoolExecutor
       } finally {
           // 释放锁
           mainLock.unlock();
       }
       // 尝试终止线程池
       tryTerminate();
   }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,233评论 6 495
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,357评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,831评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,313评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,417评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,470评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,482评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,265评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,708评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,997评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,176评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,827评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,503评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,150评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,391评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,034评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,063评论 2 352

推荐阅读更多精彩内容