关闭

ThreadPoolExecutor源码解析

标签: java源码线程池并发
627人阅读 评论(0) 收藏 举报
分类:

ThreadPoolExcutor是java并发模块中非常重要的一个线程池的实现,FixedThreadPool,CachedThreadPool和SingleThreadPool都是该pool的一个不通的参数情况下的特性。本文主要介绍一下ThreadPoolExcutor的关键参数以及常用的方法的内部实现。


1. 构造函数及关键参数

下面的代码是ThreadPoolExecutor的构造函数,这里涉及了多个相关的参数,其中corePoolSize和maximumPoolSize两个参数共同控制了线程池中线程的大小。
* corePoolSize表示的是线程池中能够长期贮存的线程数量,即使某些线程是闲置状态,
* maximumPoolSize顾名思义表示线程池中能够存在的最大线程数量。
* keepAliveTime是线程数大于corePoolSize的时候,闲置线程能够存活的时间
* BlockingQueue < Runnable > 是其内部维护的等待被执行的工作队列。
除此之外,ThreadPoolExcutor内部使用的是HashSet< Worker >去维护其线程的。

    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;
    }

2. 关键方法之execute()

我们在使用java的并发框架的提交任务的时候,一般会用到submit方法和execute方法两种, 其中submit方法的内部是调用execute方法的,因此在这只对execute()方法进行分析。
从下面的源码中可以看到这里有三个关键的步骤:
1. 当当前的worker的数量(也就是thread, Worker是Thread的封装)少于corePoolSize的时候,新建一个Worker,并将当前的任务command作为该worker的第一个任务。
2. 否则的话,理论上需要将command添加到任务队列中去,这里源码做了两次的check(), 这主要是基于两个原因:1)之前如果是有线程跪了,哪么需要再启动新的线程,2)在此期间调用了shutdown此时应该拒绝该command加入任务队列。
3. 如果不能将command加入工作队列(工作队列已满), 那么在尝试新建线程(这里读者可以仔细看一下两个addWorker的方法的不同,前一个第二个参数true表示线程数上限为corePoolSize后者则表示是maximumPoolSize),如果此时新建线程失败,则有可能是线程池处于饱和状态或者是调用了shutdown方法。

至此execute的方法就结束了,总结一下就是首先尝试在当前线程数小于corePoolSize的情况下新建线程,不成功则尝试将任务添加到任务队列,如果仍然不成功则尝试在maximumPoolSize的线程上界下新建线程,如果任然不成功则拒绝。

    public void execute(Runnable command) {
        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);
    }

3. 关键方法之runWorker

该方法是Worker内部调用的方法,也是每个线程执行的主要方法体,该方法所做的事情很简单,就是不断地从任务队列中去取job,然后执行job。

   final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
            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 {
                    beforeExecute(wt, task);
                    Throwable thrown = null;
                    try {
                        task.run();
                    } catch (RuntimeException x) {
                        thrown = x; throw x;
                    } catch (Error x) {
                        thrown = x; throw x;
                    } catch (Throwable x) {
                        thrown = x; throw new Error(x);
                    } finally {
                        afterExecute(task, thrown);
                    }
                } finally {
                    task = null;
                    w.completedTasks++;
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
            processWorkerExit(w, completedAbruptly);
        }
    }

4. 关键方法之shutdown, shutdownNow, awaitTermination

着三个方法都是线程关闭的相关方法,
* shutdown方法调用之后,线程池将会拒绝新的任务进来,但是它并不等待已经提交方法的结束,awaitTermination则会等待提交任务的结束。
* shutdownNow 将会尝试去结束所有正在执行的任务,并从队列清除等待任务,将这些等待任务作为返回值返回,shutdownNow方法也不会去等正在执行任务的完全关闭。

代码如下:

    public void shutdown() {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            checkShutdownAccess();
            advanceRunState(SHUTDOWN);
            interruptIdleWorkers();
            onShutdown(); // hook for ScheduledThreadPoolExecutor
        } finally {
            mainLock.unlock();
        }
        tryTerminate();
    }
      */
    public List<Runnable> shutdownNow() {
        List<Runnable> tasks;
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            checkShutdownAccess();
            advanceRunState(STOP);
            interruptWorkers();
            tasks = drainQueue();
        } finally {
            mainLock.unlock();
        }
        tryTerminate();
        return tasks;
    }
     public boolean awaitTermination(long timeout, TimeUnit unit)
        throws InterruptedException {
        long nanos = unit.toNanos(timeout);
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            for (;;) {
                if (runStateAtLeast(ctl.get(), TERMINATED))
                    return true;
                if (nanos <= 0)
                    return false;
                nanos = termination.awaitNanos(nanos);
            }
        } finally {
            mainLock.unlock();
        }
    }

5. 其他

ThreadPoolExecutor主体就介绍完了,接下来介绍一下如何利用ThreadPoolExecutor去构建CachedThreadPool,SingleThreadPool以及FixThreadPool。 Executors中相关代码如下:
1. CachedThreadPool中corePoolSize = 0, maximumPoolSize = Integer.MAX_VALUE, KeepAliveTime = 60 sec, 这也就是说CachedThreadPool允许按需建立新的线程,闲置的线程存活时间为60秒,因此比较适合短任务的情况这样能够复用线程。
2. SingleThreadPool 最大只能够允许1个线程的存在,当前线程跪了的情况下是允许新的线程建立并执行工作的,由于corePoolSize和maximumPoolSize的数量相同对于keepAliveTime来说多大也就没有意义
3. FixedThreadPool 的参数中corePoolSize和maximumPoolSize的数量相同,这也就固定了线程池中的线程数的大小。

  public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
0
0
查看评论

ThreadPoolExecutor解析-主要源码研究

ThreadPoolExecutor解析-主要源码研究
  • wenhuayuzhihui
  • wenhuayuzhihui
  • 2016-05-26 14:26
  • 7048

Java 线程池 ThreadPoolExecutor 源码分析

线程池能够对线程进行有效的管理, 复用和数量上限的限制, 所以比起原始的 new Thread().start() 这种创建并启动线程的方式, 线程池的效率和性能都更好. Java 中的线程池是用 ThreadPoolExecutor 类来表示的. 我们今天就结合该类的源码来分析一下这个类内部对于...
  • cleverGump
  • cleverGump
  • 2016-02-18 18:51
  • 4663

ThreadPoolExecutor源码解析(基于Java1.8)

第一部分:ThreadPoolExecutor的继承结构 根据上图可以知道,ThreadPoolExecutor是继承的AbstractExecutorService(抽象类)。再来看一下AbstractExecutorService的结构可以发现,AbstractExecutorService...
  • Rebirth_Love
  • Rebirth_Love
  • 2016-07-19 17:51
  • 2101

Java ThreadPoolExecutor线程池原理及源码分析

一、源码分析(基于JDK1.6) ThreadExecutorPool是使用最多的线程池组件,了解它的原始资料最好是从从设计者(Doug Lea)的口中知道它的来龙去脉。在Jdk1.6中,ThreadPoolExecutor直接继承了AbstractExecutorService,并层级实现了Ex...
  • scherrer
  • scherrer
  • 2016-02-21 17:27
  • 1179

JDK1.8源码分析之ThreadPoolExecutor

一、前言   JUC这部分还有线程池这一块没有分析,需要抓紧时间分析,下面开始ThreadPoolExecutor,其是线程池的基础,分析完了这个类会简化之后的分析,线程池可以解决两个不同问题:由于减少了每个任务调用的开销,它们通常可以在执行大量异步任务时提供增强的性能,并且还可以提供绑定和管...
  • qq_22929803
  • qq_22929803
  • 2016-08-28 19:52
  • 1511

Java 1.7 ThreadPoolExecutor源码解析

相比1.6,1.7有些变化: 1、        增加了一个TIDYING状态,这个状态是介于STOP和TERMINATED之间的,如果执行完terminated钩子函数后状态就变成TERMINATED了; 2、&#...
  • yuenkin
  • yuenkin
  • 2016-04-01 23:04
  • 1634

java并发编程之源码分析ThreadPoolExecutor线程池实现原理

本文从线程池的创建、核心线程数、最大线程数、线程的创建、任务队列、拒绝策略、工作线程keep-alive等方面详细解读了线程池特性。然后介绍了线程池的状态以及线程池的实现原理,详解了addWorker、runWorker、getTask核心方法的实现。
  • prestigeding
  • prestigeding
  • 2016-12-29 16:25
  • 1873

JDK 源码解析 —— Executors ExecutorService ThreadPoolExecutor 线程池

零. 简介 Executors 是 Executor、ExecutorService、ThreadFactory、Callable 类的工厂和工具方法。 一. 源码解析 创建一个固定大小的线程池:通过重用共享无界队列里的线程来减少线程创建的开销。当所有的...
  • wenniuwuren
  • wenniuwuren
  • 2016-05-16 22:28
  • 3439

源码解析 ThreadPoolExecutor JAVA1.8

1. 构造函数 corePoolSize:池里维持的最小线程数,即使它们是空闲线程,也不会进行销毁 maximumPoolSize:最大线程数 keepAliveTime:当池里的线程数量超过了corePoolSize时,如果额外线程在keepAliveTime时间段内都未执行新任务,将被销毁 核...
  • pomer_huang
  • pomer_huang
  • 2017-12-22 00:04
  • 47

线程池ThreadPoolExecutor源码解析

最近将ThreadPoolExecutor源码又读了一遍,将以前没有弄的太懂的地方给弄懂了点。所以写下这篇博客记录一下自己的理解。          在看源码之前得知道线程池中几个参数的作用和线程池大体的工作原理,这样才能更好的理解源码。 一.线程池...
  • wangbiao007
  • wangbiao007
  • 2017-10-10 19:33
  • 115
    个人资料
    • 访问:61322次
    • 积分:1444
    • 等级:
    • 排名:千里之外
    • 原创:83篇
    • 转载:1篇
    • 译文:0篇
    • 评论:2条
    文章分类
    最新评论