线程池ThreadPoolExecutor源码解析

转载请标明出处:
https://blog.csdn.net/bingospunky/article/details/80234457
本文出自马彬彬的博客

创建ThreadPoolExecutor

ExecutorService fixedThreadPool = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(1));

我们使用Executors.newXXX方法创建线程池也是通过上面的new方法进行创建的,只是传递的初始化参数不同罢了。

第1、2个参数是线程池线程的数量,第3、4个参数是当线程没有runnable执行时,多长时间结束,第5个参数是当runnable多于线程数时,把runnable存放的队列。

原理

ThreadPoolExecutor有一个private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));参数表示当前执行的线程,ctl是一个32位的整数,AtomicInteger是为了线程安全。这个ctl前3位相当于占位功能,后29位表示当前启动的线程数量,比如11100000000000000000000000000000表示没有执行的线程,11100000000000000000000000000001表示当前有一个线程在执行。当添加线程或者线程结束时,会响应的改变ctl的值。

有一个Worker的类,这个Worker继承AbstractQueuedSynchronizer,实现锁的功能,在执行runnable时互斥。这个Worker包含一个Thread,Thread的run方法是while循环,不断的从queue里获取runnable执行。

当我们执行的runnable数量 超过线程数+queue.size 时如何

当我们使用Executors.newXXX创建线程池时一般不会遇到这个问题,因为这样创建的队列容量是Integer.MAX,当我们想控制线程池的queue容量时,就有可能会遇到超过上限的这种情况。

当出现这种情况时,会抛出异常。execute方法代码如下:

Code1
ThreadPoolExecutor.ThreadPoolExecutor

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

我就不自己解释了,借用一下代码里的一段注释来解释。

/*
 * 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.
 */

当我们的runnable出现异常时如何处理的

从使用经验上来看,我们的runnable方法抛出了异常,后面的runnable依然可以执行。那么这是如何实现的呢。

首先来看Worker里的Thread的run方法。

Code2
ThreadPoolExecutor.runWorker

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

Code2第19行抛出异常后,会执行第37行的processWorkerExit方法,这时completedAbruptly=true,表示这时异常结束的前面的while循环。我们再看一下processWorkerExit方法的代码。

Code3
ThreadPoolExecutor.processWorkerExit

    private void processWorkerExit(Worker w, boolean completedAbruptly) {
        if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
            decrementWorkerCount();
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            completedTaskCount += w.completedTasks;
            workers.remove(w);
        } finally {
            mainLock.unlock();
        }
        tryTerminate();
        int c = ctl.get();
        if (runStateLessThan(c, STOP)) {
            if (!completedAbruptly) {
                int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
                if (min == 0 && ! workQueue.isEmpty())
                    min = 1;
                if (workerCountOf(c) >= min)
                    return; // replacement not needed
            }
            addWorker(null, false);
        }
    }

由于completedAbruptly值为true,先执行第3行代码,改变前文中提到的ctl的值,让其表示的线程数减1。Code3第8行,remove掉该Worker。会执行Code3第22行代码,使用runnable=null,添加一个Worker对象,这句话的意思就是启动一个新的线程,去queue里获取runnable执行。

所以,我们自己runnable抛出了异常,是kill掉了执行它的线程,并且补充一个新的线程。

ThreadPoolExecutor构造方法里的第3、4个方法如何生效

ThreadPoolExecutor第3、4个方法表示当线程空闲多久后kill。

这个线程kill是针对多余core线程数生效的,或则我们可以设置对于所有线程生效。如果设置对于所有线程生效,我们需要执行ThreadPoolExecutor.allowCoreThreadTimeOut(true)设置。

那么这是如何生效的呢。我们还是来看Code2的代码。该线程一直在while里执行,当它跳出while时,就是线程结束的条件。跳出while的条件就是Code2第8行的getTask方法返回null。接下来看getTask方法。

Code4
ThreadPoolExecutor.getTask

    private Runnable getTask() {
        boolean timedOut = false; // Did the last poll() time out?
        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);
            // Check if queue empty only if necessary.
            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
                decrementWorkerCount();
                return null;
            }
            int wc = workerCountOf(c);
            // Are workers subject to culling?
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
            if ((wc > maximumPoolSize || (timed && timedOut))
                && (wc > 1 || workQueue.isEmpty())) {
                if (compareAndDecrementWorkerCount(c))
                    return null;
                continue;
            }
            try {
                Runnable r = timed ?  workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : workQueue.take();
                if (r != null)
                    return r;
                timedOut = true;
            } catch (InterruptedException retry) {
                timedOut = false;
            }
        }
    }

Code4第13行,当allowCoreThreadTimeOut或者当先线程数超过core线程数时,设置timed=true。Code第21行,当timed==true时,queue获取runnable时阻塞我们设置的时间。这样来实现当线程空闲某一段时间后,线程kill的。

补充

ThreadPoolExecutor记录线程数量等使用的是一个AtomicInteger类型的变量ctl,把这个变量前3位和后29位分开使用的,使用了位运算,下面的代码方面我们查看数据的变化。

    System.out.println(-536870912 & 536870911);
    System.out.println(-536870912 & ~536870911);
    System.out.println(Integer.toBinaryString(-536870912));
    System.out.println(Integer.toBinaryString(-536870911));
    System.out.println(Integer.toBinaryString(536870911));
    System.out.println(Integer.toBinaryString(536870912));
    System.out.println(Integer.toBinaryString(-1));
    System.out.println(Integer.toBinaryString(-1));
    System.out.println(Integer.toBinaryString(-1 << 29));
    System.out.println(Integer.toBinaryString((-1 << 29) | 0));
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值