java源码学习-线程池ThreadPoolExecutor

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方法从阻塞队列中获取
            while (task != null || (task = getTask()) != null) {
                w.lock();
                // 当前线程池是否大于STOP状态, 或者当前线程需要中断并清除中断状态, 并且
                // 这时的线程池状态是大于STOP的
                // 如果上述条件都满足, 那么检查该对象是否处于中断状态, 如果不处於则设置中断
                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);

            // 判断工作线程个数是否大于核心线程个数, 这里的情况是阻塞队列曾经满过, 直接开新线程
            // allowCoreThreadTimeOut是默认为false的, 我们假设他这里也为false
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
			
			// 如果工作线程数量大于最大线程数量, 或者
			// timed是true并且timedOut是true(这里timedOut默认为false)
			// 并且工作线程个数大于1或者阻塞队列为空
            if ((wc > maximumPoolSize || (timed && timedOut))
                && (wc > 1 || workQueue.isEmpty())) {
                // 满足条件就将wc-1, 因为wc大于最大线程数量或者已经超时
                if (compareAndDecrementWorkerCount(c))
                    return null;
                continue;
            }

            try {
            	// timed为true则将阻塞队列的队列头出列, 这里就是阻塞队列中的任务获得过程
            	// 返回到runWorker的while中执行
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
                if (r != null)
                	// 返回r, 可以给Worker执行
                    return r;
                timedOut = true;
            } catch (InterruptedException retry) {
                timedOut = false;
            }
        }
    }

后记

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章