关闭

ThreadPoolExecutor源码解析

标签: java源码线程池并发
520人阅读 评论(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

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:52170次
    • 积分:1346
    • 等级:
    • 排名:千里之外
    • 原创:83篇
    • 转载:1篇
    • 译文:0篇
    • 评论:2条
    文章分类
    最新评论