java线程池JVM源码解析

 

java线程池JVM源码解析

概述

对于java的线程池,刚开始接触的时候可能就知道使用Executors.newFix……之类的方法,java线程池有三种;

固定大小的

无限伸展的

单个的single

还有就是大于核心池子怎么样。其实际上这个三个池子都是基于java提供的最基本的  ThreadPoolExecutor 进行开发的,只是传递了不同的参数而已。

线程池出现的背景(线程复用)

线程池也是一种池化技术,主要是为了实现资源的复用,即需要创建很多的线程去执行任务的时候,不必要为每个任务创建一个线程,这样有两个缺点:

1线程的数量不好控制,容易增加jvm的内存消耗

2没创建一个线程后,在当线程运行完毕后,都需要操作系统对其进行创建--->执行---->销毁增加了操作系统的负担。

使用线程池能够克服上述缺点:

1 线程池能够将线程的数量控制在一定的范围内,可以达到控制的目的,不会轻易消耗完jvm的内存

2 线程池中的线程可以复用,即一个线程一旦创建,该线程可以执行很多任务,而不需要为每个任务(Runnable对象)创建线程

核心线程池 ThreadPoolExecutor

java中所有的线程池都是基于ThreadPoolExecutor开发出来的,因此好多developer都是基于该基类来实现自己的线程池(开源的组件hive、oozie等)。线程池执行任务的时候提供的接口是execute(Runnable runable),将对象一直往池子里面装,池子一直在消耗里面的任务。下面分析execute的方法

public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        /*
         * Proceed in 3 steps:
         *
         * 1. If fewer than corePoolSize threads are running, try to
         * start a new thread with the given command as its first
         * task.  The call to addWorker atomically checks runState and
         * workerCount, and so prevents false alarms that would add
         * threads when it shouldn't, by returning false.
         *
         * 2. If a task can be successfully queued, then we still need
         * to double-check whether we should have added a thread
         * (because existing ones died since last checking) or that
         * the pool shut down since entry into this method. So we
         * recheck state and if necessary roll back the enqueuing if
         * stopped, or start a new thread if there are none.
         *
         * 3. If we cannot queue task, then we try to add a new
         * thread.  If it fails, we know we are shut down or saturated
         * and so reject the task.
         */
        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);
    }

看这段源码之前,先需要明白一个东西:线程池状态的判断方法、线程池中线程个数的判断方法

int c = ctl.get();//AtomicInteger的get方法,取出其中的数值
workerCountOf(c)//判断当前线程中有几个正在running的线程
isRunning(c)//线程池的状态

前期预备知识

这三行代码的作用再注释中已经标明,但是这里主要看下这个是怎么实现的

 // Packing and unpacking ctl
    private static int runStateOf(int c)     { return c & ~CAPACITY; }
    private static int workerCountOf(int c)  { return c & CAPACITY; }
    private static int ctlOf(int rs, int wc) { return rs | wc; }

实际上就是进行一系列的位运算,具体也不清楚怎么算的。

线程池执行流程

上面的代码来源于java的 系统类加载器加载的 rt.jar中的ThreadPoolExecutor源码。往线程池中添加任务的时候,主要分为三个if判断:

第一个if

if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }

这段代码的意思是:

当前线程池中的线程个数<corePoolSize(核心池)大小的时候,直接执行addWorker方法,为任务对象(Runnable对象command)创建新的线程,并且更新c的数量加一(池子中线程数量增加一个,具体逻辑再addWorker中)

这里addWorker方法中传递的为true,即往核心池子里面加(后面的addWorker方法会描述),即小于coresize大小就行

第二个if

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

如果当前线程池的状态为RUNNING状态,并且任务对象command也成功的插入到队列中了。

接下来再次判断线程池的状态:

如果此次发现线程池的状态出现异常,并且刚才最外层if插入到队列中的任务成功的出队,这个时候执行拒绝策略

通俗的意思就是当前线程池出现异常,就把刚才入队的任务执行拒绝策略,并抛异常出去

else  if

如果线程池状态正常,或者当前队列中没有成功吧这个任务移除,(逻辑与翻转后应该变成或运算),那就说明这个任务已经被其他的线程从队列中拿走了  【这是else逻辑】

并且当前运行的线程数量为0(workerCountOf(recheck) == 0),那么这个时候意味着:

当前的任务已经被执行了,而且池子中的线程也已经全部被销毁了,但是队列中是个啥情况还不知道,因此

这个时候执行addWorker,注意此时的addWorker方法中的command是个null,即addWorker(null, false);  这个方法的含义是消耗队列中的任务,一直到队列为空(线程复用也就在这里体现)。

这里的addWorker方法传递的是falses,即往最大池子里面添加,即小于maxsize就行

其实就是一个收尾的过程

 

最后一个if

else if (!addWorker(command, false))
            reject(command);

当线程池不在RUNNING或者是插入队列失败的情况下:再次执行一个addWorker(command),如果addWorker也失败,执行拒绝策略。如果执行成功,则创建一个新的线程执行当前任务和消耗队列的任务。

线程池不在RUNNING,addWorker方法内部会首先判断这个状态,如果不在RUNNIN,直接返回false,因此最后一个if中如果线程池不在RUNNING,则addWorker必然失败。

如果是因为queue.offer方法失败了,那则是因为队里满了,所以这个时候会再创建一个worker来执行当前command,并且帮着其他的worker一起消耗队里里面的runnable。

addWorker方法

这个方法是整个线程池中真正添加线程和线程复用的核心

private boolean addWorker(Runnable firstTask, boolean core) {
        retry:
        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);

            // Check if queue empty only if necessary.
            if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                return false;

            for (;;) {
                int wc = workerCountOf(c);
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                if (compareAndIncrementWorkerCount(c))
                    break retry;
                c = ctl.get();  // Re-read ctl
                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;
            w = new Worker(firstTask);
            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();
                        workers.add(w);
                        int s = workers.size();
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                        workerAdded = true;
                    }
                } finally {
                    mainLock.unlock();
                }
                if (workerAdded) {
                    t.start();
                    workerStarted = true;
                }
            }
        } finally {
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;
    }

上面是jvm中addWorker方法方法,这里分两个部分来分析

1 线程池判断部分

retry:
        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);

            // Check if queue empty only if necessary.
            if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                return false;

            for (;;) {
                int wc = workerCountOf(c);
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                if (compareAndIncrementWorkerCount(c))
                    break retry;
                c = ctl.get();  // Re-read ctl
                if (runStateOf(c) != rs)
                    continue retry;
                // else CAS failed due to workerCount change; retry inner loop
            }
        }

 第一小部分

if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                return false;

该部分的意思是:判断线程池的状态

    private static final int RUNNING    = -1 << COUNT_BITS;
    private static final int SHUTDOWN   =  0 << COUNT_BITS;
    private static final int STOP       =  1 << COUNT_BITS;
    private static final int TIDYING    =  2 << COUNT_BITS;
    private static final int TERMINATED =  3 << COUNT_BITS;

如果:

线程池状态异常 (STOP、TIDYING、TERMINATED),并且下面的两个满足其中一个

      任务 firstTask 不为空(不消耗队列)或者

      队列为空

则addWorker方法错误,执行拒绝策略。

通俗的意思就是 当前线程池状态异常了 

如果你还往里面添加command(对应任务 firstTask 不为空),则执行拒绝策略。线程池都异常了,还提交啥任务

如果当前command为空但是队列已经空了(对应上述队列为空),则也执行拒绝策略。线程池都异常了,addWorker传进来null本来是消耗队列里面的任务,结果队列都空了,那还执行啥,拒绝策略。

第二小部分

for (;;) {
                int wc = workerCountOf(c);
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                if (compareAndIncrementWorkerCount(c))
                    break retry;
                c = ctl.get();  // Re-read ctl
                if (runStateOf(c) != rs)
                    continue retry;
                // else CAS failed due to workerCount change; retry inner loop
            }

这个线程池中的coresize和maxsize只在这一处体现。

如果addWorker里面传递是true,则比较当前的线程池中的线程数量和coresize比较,如果大于core则执行addWorker失败

通俗的意思就是,前面执行addWorker的时候如果选择往核心池子里面加,就要判断核心池子是否越界了。

这里肯能会有疑问说,添加的时候选择true必然知道当前的数量是小于coresize的,为什么addWorker的时候还要判断一次呢?

这是因为,真正要添加的时候,线程池里面的数量可能已经很多了,毕竟添加线程这个活动不会根据第一次的结果,可能线程池一直在往里面塞任务。

如果addWorker传递的是false,则检查maxsize的数量。如果发现数量和刚开始的不一样了,则继续循环,说明当前有线程退出了。需要重新再执行一遍。

第三小部分

这里才进入到了真正的创建线程的部分。

 boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
            final ReentrantLock mainLock = this.mainLock;
            w = new Worker(firstTask);
            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();
                        workers.add(w);
                        int s = workers.size();
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                        workerAdded = true;
                    }
                } finally {
                    mainLock.unlock();
                }
                if (workerAdded) {
                    t.start();
                    workerStarted = true;
                }
            }
        } finally {
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;

这段代码看似很多,这里只说下关键的部分

下面是真正添加线程的部分,线程池中将线程包装成了一个Worker对象

 w = new Worker(firstTask);
final Thread t = w.thread;

 

然后更新alivecount,这个数量是有线程池中的hashset来维护的

 workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;

线程池中的线程执行

if (workerAdded) {
t.start();
workerStarted = true;
                }

本次就到这吧,详情看下一篇java线程池中的Worker解析

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值