java源码学习-线程池ThreadPoolExecutor

在开始之前得先了解线程池创建线程以及使用的流程

  1. 根据corePoolSize创建初始线程, 只要有新的任务委派给线程池执行的, 就会通过线程工厂(Executors)创建一个新的线程
  2. 如果当前线程数=corePoolSize, 那么就将后续的任务加入阻塞队列, 等待worker完成任务之后从阻塞队列的队列头开始将任务出列执行
  3. 如果阻塞队列已经满了, 那么就会根据设置的maximumPoolSize来判断, 如果当前线程数小于maximumPoolSize则通过线程工厂创建新线程, 否则进行饱和策略
  4. 饱和策略就是拒绝方式, 可以选择抛出异常, 忽略, 放弃时间最早的任务转而执行新任务, 直接由提交任务的线程执行任务这四种拒绝方式

这里是我自制的execute方法流程图
在这里插入图片描述

main方法

先贴上我做实验使用的main方法

public class ThreadPoolExecutorTest {

    public static void main(String[] args) {
        int corePoolSize = 2;
        int maximumPoolSize = 4;
        BlockingQueue<Runnable> blockingDeque = new ArrayBlockingQueue<>(2);
        RejectedExecutionHandler handle = new ThreadPoolExecutor.AbortPolicy();
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, 0,
                TimeUnit.SECONDS, blockingDeque, handle);

        Thread[] threads = new Thread[6];
        for(int i = 0; i < 6; i ++) {
            int finalI = i;
            threads[i] = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        System.out.println("start: " + finalI);
                        Thread.sleep(1000);
                        System.out.println("end: " + finalI);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
        for(Thread thread: threads) {
            threadPoolExecutor.submit(thread);
        }
        System.out.println(threadPoolExecutor.getActiveCount());
        threadPoolExecutor.shutdown();
    }
}

构造器

这里只贴一个, 其他构造方法都是直接使用这个的

参数名称 作用
corePoolSize 初始线程池的线程个数
maximumPoolSize 线程池中最大线程个数
keepAliveTime 线程存活时间
unit 存活时间的单位
workQueue 一个阻塞队列
threadFactory 线程工厂
handler 采取的饱和政策(拒绝的策略)
拒绝策略 行为
AbortPolicy 拒绝时会抛出RejectedExecutionException异常
DiscardPolicy 直接忽略
DiscardOldestPolicy 丢弃执行队列中最老的任务, 尝试提供位置给当前任务
CallerRunsPolicy 交给任务提交者执行

拒绝策略默认为AbortPolicy, 构造器中前五个参数都是必填参数, 线程工厂默认为Executors中的defaultThreadFactory

    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.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

Worker内部类

Worker继承了AQS抽象队列同步器, 以及实现了Runnable接口, 可以说是一个具有锁特性的可执行任务类

private final class Worker
        extends AbstractQueuedSynchronizer
        implements Runnable
    {
        /**
         * This class will never be serialized, but we provide a
         * serialVersionUID to suppress a javac warning.
         */
        private static final long serialVersionUID = 6138294804551838833L;

        /** Thread this worker is running in.  Null if factory fails. */
        final Thread thread;
        /** Initial task to run.  Possibly null. */
        Runnable firstTask;
        /** Per-thread task counter */
        volatile long completedTasks;

        // 构造器, 从工厂中获取新线程
        Worker(Runnable firstTask) {
            setState(-1); // inhibit interrupts until runWorker
            this.firstTask = firstTask;
            this.thread = getThreadFactory().newThread(this);
        }

        // 这个任务的run方法调用在ThreadPoolExecutor中的runWorker, 后面会写到
        public void run() {
            runWorker(this);
        }

        // Lock methods
        //
        // The value 0 represents the unlocked state.
        // The value 1 represents the locked state.
		// 以下都是重写AQS方法来实现锁
        protected boolean isHeldExclusively() {
            return getState() != 0;
        }

        protected boolean tryAcquire(int unused) {
            if (compareAndSetState(0, 1)) {
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }

        protected boolean tryRelease(int unused) {
            setExclusiveOwnerThread(null);
            setState(0);
            return true;
        }

        public void lock()        { acquire(1); }
        public boolean tryLock()  { return tryAcquire(1); }
        public void unlock()      { release(1); }
        public boolean isLocked() { return isHeldExclusively(); }

        void interruptIfStarted() {
            Thread t;
            if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
                try {
                    t.interrupt();
                } catch (SecurityException ignore) {
                }
            }
        }
    }

executor

executor是将任务提交到线程池, 这是使用线程池的第二步, 第一步当然是new一个线程池
这个方法里面有三个判断

  1. 当前正在执行的任务小于corePoolSize, 也就是线程池还有空余, 那么直接执行任务, 否则进入2
  2. 将当前任务放入阻塞队列, 等待直至线程池中有空闲线程
  3. 如果阻塞队列满了, 就会判断之前设置的maximumPoolSize最大线程个数, 如果当前执行的任务个数小于maximumPoolSize, 那么就会创建新线程去执行这个任务
    public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        // ctl初始值是一个很大的负数, 这个负数同CAPACITY进行与运算可以得出正在运行的任务个数
        int c = ctl.get();
        // 这是第一个判断
        if (workerCountOf(c) < corePoolSize) {
        	// 在addWorker中通过true和false来判断这是第一步还是第三步
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        // isRunning方法用于判断线程池是否还在工作
        // 如果还在工作就将任务加入阻塞队列
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            // 重新进行工作检测, 在并发情况下可能会出现工作状况被别的线程修改的情况
            // 如果线程池结束工作了并且阻塞队列中移除当前任务成功, 则进行饱和策略
            if (! isRunning(recheck) && remove(command))
                reject(command);
            // 否则判断当前正在执行的任务个数是不是0
            // 如果是则添加一个空任务
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        // 阻塞队列满了, 第三步
        else if (!addWorker(command, false))
        	// 添加任务失败则进行饱和策略
            reject(command);
    }

addWorker

这个方法是线程池中很重要的方法, 用于执行任务

private boolean addWorker(Runnable firstTask, boolean core) {
		// break标记
        retry:
        for (;;) {
            int c = ctl.get();
            // 获取当前线程池状态
            int rs = runStateOf(c);

            // 如果rs是终止状态或者其他三种状态(这些状态都不接受新任务)并且
            // rs不是终止状态或者传入的任务为空, 或者阻塞队列为空
            // 就会进入语句块, 返回false
            if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                return false;

            for (;;) {
                int wc = workerCountOf(c);
                // wc是线程正在执行的任务个数
                // 如果wc大于线程池所能承受的最大数量CAPACITY = 536,870,911(二进制中29个1)
                // 否则, 看是executor的第几步来判断是否大于corePoolSize 或者maximumPoolSize
                // 如果上述两个条件满足一个则返回false
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                // 使用cas操作将正在执行的任务+1, 成功则跳出最外侧循环
                if (compareAndIncrementWorkerCount(c))
                    break retry;
                // cas失败了, 则更新c的数值
                c = ctl.get();  // Re-read ctl
                // 判断rs状态是否改变
                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 {
        	// 创建一个新任务, 能来到这一步的都不可能是进入阻塞队列的任务, 所以需要新建一个工作
            w = new Worker(firstTask);
            final Thread t = w.thread;
            if (t != null) {
                final ReentrantLock mainLock = this.mainLock;
                // 这里要获取线程池的锁, 为了防止在创建工作时线程池状态被改变
                mainLock.lock();
                try {
                    // 获取rs的状态
                    int rs = runStateOf(ctl.get());
					// 这里不需要进行跟上面一样的重复判断, 因为这里是同步的
                    if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) {
                        // 判断t线程是否已经存在, 因为这里需要启动线程
                        if (t.isAlive())
                            throw new IllegalThreadStateException();
                        // 将w加入到workers中, workers是一个被final修饰的hashSet
                        // 这里提一句被final修饰的类或者对象我们是可以改变其内部值的
                        // final修饰对象时仅仅是不可改变其引用地址
                        // 因为workers的所有方法在使用前都会加上锁, 所以这里可以直接获取size
                        workers.add(w);
                        int s = workers.size();
                        // largestPoolSize初始是0, 用于记录最大的同时执行任务数
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                        workerAdded = true;
                    }
                } finally {
                    mainLock.unlock();
                }
                // 任务添加成功就执行线程
                if (workerAdded) {
                    t.start();
                    workerStarted = true;
                }
            }
        } finally {
        	// 任务执行失败就调用添加任务失败方法来移除任务
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;
    }

runWorker

在runWorker中已经创建的Worker会一直存在, 并且会自旋, 获取阻塞队列中的任务执行就是在这个方法中, 个人理解Worker最大值就是之前设置的最大线程数

final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
        	// 这里的循环如果初始传入的Worker中无任务, 则使用getTask方法从阻塞队列中获取
        	// getTask方法里面如果返回null则会中止线程, keepAliveTime参数在这里有用到
            while (task != null || (task = getTask()) != null) {
                w.lock();
                // 当前线程池是否大于STOP状态, 或者当前线程需要中断并清除中断状态, 并且
                // 这时的线程池状态是大于STOP的
                // 如果上述条件都满足, 那么检查该对象是否处于中断状态, 如果不处于则设置中断
                // 这里是保证线程池处于中断状态的时候, 一定设置了线程的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, 该Worker的完成任务数量+1
                    task = null;
                    w.completedTasks++;
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
        	// task为空并且阻塞队列中无任务则关闭该Worker
            processWorkerExit(w, completedAbruptly);
        }
    }

不难发现, 只要阻塞队列中还有任务, 已经存在的Worker就不会关闭, 而是去执行阻塞队列中的任务

private void processWorkerExit(Worker w, boolean completedAbruptly) {
		// 这个标志在上文中, 如果是正常的try流程则会为false
		// 如果是异常了就会是true, 这里就会进入decrementWorkerCount方法
		// 使用cas操作将ctl- 1
        if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
            decrementWorkerCount();

        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
        	// 更新总完成数, 在hashSet中移除该Worker
            completedTaskCount += w.completedTasks;
            workers.remove(w);
        } finally {
            mainLock.unlock();
        }
		// 判断线程池状态尝试终止线程池
        tryTerminate();

        int c = ctl.get();
        // 如果当前线程池状态是不需要被关闭的, 即小于STOP的
        if (runStateLessThan(c, STOP)) {
        	// 判断是否正常中止, 如果是则说明所有任务都已完成就进入该语句块
            if (!completedAbruptly) {
            	// min默认是corePoolSize, 如果允许超时则是0
                int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
                // 如果min是0阻塞队列是不为空的, 那么必须要保证有一个工作线程
                if (min == 0 && ! workQueue.isEmpty())
                    min = 1;
                // 如果当前线程数量大于等于最小工作线程数量则直接返回
                if (workerCountOf(c) >= min)
                    return; // replacement not needed
            }
            // 因为不是正常中止, 所以该Worker不该被中止, 添加一个空的Worker
            // 这样可以继续执行阻塞队列中的任务
            addWorker(null, false);
        }
    }

getTask

private Runnable getTask() {
        boolean timedOut = false; // Did the last poll() time out?
		// 这是一个死循环, 即必须返回一个元素
        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);

            // 判断线程池状况, 如果下面情况同时满足则将ctl - 1
            // 1.状态>=SHUTDOWN, 即需要被中止
            // 2.状态>=STOP, 即需要被中止并且不完成已添加的任务, 或者阻塞队列为空
            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
                decrementWorkerCount();
                return null;
            }
			// 获取工作线程个数
            int wc = workerCountOf(c);

            // 判断工作线程个数是否大于核心线程个数, 那临时线程是可以超时销毁的, 所以timed是true
            // allowCoreThreadTimeOut参数为是否允许核心线程超时, 默认为false
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
			
			// 如果工作线程数量大于最大线程数量, 或者
			// timed是true并且timedOut是true(允许销毁核心线程或者临时线程的poll等待超时)
			// 并且工作线程个数大于1或者阻塞队列为空
			// 如果阻塞队列非空那么一定要保证有多余一个线程可以执行任务, 所以做这么个判断
            if ((wc > maximumPoolSize || (timed && timedOut))
                && (wc > 1 || workQueue.isEmpty())) {
                // 满足条件就将wc-1, 因为wc大于最大线程数量或者已经超时
                if (compareAndDecrementWorkerCount(c))
                    return null;
                continue;
            }

            try {
            	// timed为true则将阻塞队列用poll加上等待时间出列,返回到runWorker的while中执行
            	// 如果是核心线程则默认不销毁,直接使用take()
            	// 这里的poll是根据参数keepAliveTime来判断, 若等待时间超出
            	// 即阻塞队列中无任务的时间超出keepAliveTime就返回null,
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
                if (r != null)
                	// 返回r, 可以给Worker执行
                    return r;
                // 这里设置timedOut=true, 然后重新自旋
                timedOut = true;
            } catch (InterruptedException retry) {
                timedOut = false;
            }
        }
    }

后记

在学习ThreadPoolExecutor源码的过程中遇到最大的问题就是, 一直在想阻塞队列中的任务是如何执行的, 线程池为什么可以节省创建线程的时间, 线程池中是否创建的时候就自带线程, 这些问题在学习完源码之后就都解决了

猜你喜欢

转载自blog.csdn.net/Cia_zibo/article/details/105759694