【Java并发编程实战】——Java线程池ThreadPoolExecutor(二)

Executors 框架最核心的类就是 ThreadPoolExecutor,上篇文章中提到的几个线程池均使用了它做为线程池的实现类。

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>());
}
public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
          new DelayedWorkQueue());
}

可以看到都是构造了一个 ThreadPoolExecutor 对象,只是参数不同,接下来看看 ThreadPoolExecutor 具体的参数

    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;
    }
  • corePoolSize: 核心线程池大小。提交一个任务到线程池就会创建一个线程执行任务,即使有其他空闲线程也继续创建,直到线程数量等于 corePoolSize 就不再创建。
  • maximumPoolSize: 最大线程池大小。如果核心线程繁忙且阻塞队列也满了,就创建新的线程执行任务,直到线程总数量达到 maximumPoolSize。如果是无界阻塞队列,这个参数就没啥用了。
  • keepAliveTime:当线程池线程数量超过核心线程池时,多余线程在被销毁前等待新任务的最大时间,即这部分线程空闲多长时间被销毁。
  • unit: keepAliveTime 的单位。
  • workQueue: 阻塞队列,核心线程池繁忙,保存被提交但还没执行的任务,详情见 BlockingQueue阻塞队列
  • threadFactory: 线程工厂,用于创建线程,这里可以自定义线程名称、组、优先级等。
  • handler: 拒绝策略。

当任务太多,线程池数量和阻塞队列都满了,这个时候需要采用一种策略来处理新任务。JDK 提供以下四种策略

  • AbortPolicy:直接抛异常
  • CallerRunsPolicy:只要线程池未关闭,在调用者线程中执行此任务。这样就不会丢弃任务,但会影响提交线程的性能。
  • DiscardPolicy:直接丢弃,不做任何处理。
  • DiscardOldestPolicy:丢弃最老的一个任务,重新提交这个任务,在线程池关闭的情况下该任务会被丢弃。

线程池的基本实现原理

  1. 判断当前运行的线程数量是否小于 corePoolSize 。否,则创建一个新的工作线程来执行任务。是,进行下一步。
  2. 如果继续提交任务,运行的线程数量达到了 corePoolSize,判断工作队列是否已经满了。如果没有满,新提交的任务存储到工作队列中。如果满了,进行下一步。
  3. 判断当前运行的线程数量是否达到 maximumPoolSize。否,则创建一个新的工作线程来执行任务。是,则交给拒绝策略来处理这个任务。

处理流程图
在这里插入图片描述

提交一个任务到线程池中

Future<?> submit = threadPoolExecutor.submit(thread);

public Future<?> submit(Runnable task) {
    if (task == null) throw new NullPointerException();
    RunnableFuture<Void> ftask = newTaskFor(task, null);
    //这里就是执行任务提交过程的详细步骤。
    execute(ftask);
    return ftask;
}

首先了解下 ThreadPoolExecutor 是如何表示线程池状态的。
原子ctl 利用 Integer 的 32 位,混合表示线程池的状态和已经存在的线程数量。
使用高 3 位表示线程池状态,通过 c & ~CAPACITY 计算可得到状态;
使用低 29 位表示已经存在的线程个数,通过 c & CAPACITY 计算可得到数量。

c = ???00000 00000000 00000000 00000000
CAPACITY = 00011111 11111111 11111111 11111111
~CAPACITY = 11100000 00000000 00000000 00000000
c & ~CAPACITY = ???

public class ThreadPoolExecutor extends AbstractExecutorService {
    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    private static final int COUNT_BITS = Integer.SIZE - 3; //29位
    private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

    // runState is stored in the high-order bits
    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;

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

	//特殊操作需要加锁,例如增加线程数量、关闭线程池
    private final ReentrantLock mainLock = new ReentrantLock();
    //等待队列用于支持 awaitTermination
    private final Condition termination = mainLock.newCondition();
    //线程池中的所有工作线程
    private final HashSet<Worker> workers = new HashSet<Worker>();
    //记录下线程池达到过的最大值
    private int largestPoolSize;
    //已完成任务数
    private long completedTaskCount;
    //是否允许核心线程超时
    private volatile boolean allowCoreThreadTimeOut;
    //默认拒绝策略(抛异常)
    private static final RejectedExecutionHandler defaultHandler =
        new AbortPolicy();
}

重点看下 ThreadPoolExecutor 中的 execute 方法

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);
        }
        //核心线程满了,入队列也失败,尝试创建一个新线程,直到达到设定的最大线程数量
        //注意这里传了一个 false 代表不是 corePoolSize 而是 maximumPoolSize
        else if (!addWorker(command, false))
        	//创建失败,执行拒绝策略
            reject(command);
    }

如何增加一个工作线程

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

        // Check if queue empty only if necessary.
        //线程池调用了关闭方法不管是有序关闭还是立即关闭,状态会 >=SHUTDOWN
        //线程被有序关闭,且 firstTask 为空,且阻塞队列不为空,这种情况不能返回 false
        //因为在阻塞队列不为空的情况下,调用 shutdown() 关闭线程池是需要继续执行队列中的任务的
        //如果线程池中一个线程也没有,那么这些任务是执行不了的,线程池也就不会关闭
        //firstTask 为空就是要增加一个工作线程,这种情况必须保证增加成功
        if (rs >= SHUTDOWN &&
            ! (rs == SHUTDOWN &&
               firstTask == null &&
               ! workQueue.isEmpty()))
            //线程池被关闭,不允许增加新任务
            return false;

        for (;;) {
        	//获取线程池中线程数量
            int wc = workerCountOf(c);
            //如果此时工作线程数量大于最大阀值,或者大于线程池设定的最大值,需要返回 false
            if (wc >= CAPACITY ||
                wc >= (core ? corePoolSize : maximumPoolSize))
                return false;
            //CAS 工作线程数量计数加一
            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 {
    	//构建一个工作线程
        w = new Worker(firstTask);
        final Thread t = w.thread;
        if (t != null) {
            final ReentrantLock mainLock = this.mainLock;
            //获取锁,防止并发操作,选用锁而不是某种并发集效果更好,简单不易出错
            mainLock.lock();
            try {
                // Recheck while holding lock.
                // Back out on ThreadFactory failure or if
                // shut down before lock acquired.
                int rs = runStateOf(ctl.get());
                //线程没有被关闭
                //或者在调用 shutdown 方法关闭线程池时,线程池线程数量为0的情况下需要增加一个线程
                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(Runnable firstTask) {
	//禁止中断直到运行了 runWorker 方法
    setState(-1); // inhibit interrupts until runWorker
    this.firstTask = firstTask;
    //通过线程工厂创建线程,它将自己传递给了创建的线程作为一个执行命令
    this.thread = getThreadFactory().newThread(this);
}

线程工厂默认是 Executors.defaultThreadFactory();

    public static ThreadFactory defaultThreadFactory() {
        return new DefaultThreadFactory();
    }

    static class DefaultThreadFactory implements ThreadFactory {
        private static final AtomicInteger poolNumber = new AtomicInteger(1);
        private final ThreadGroup group;
        private final AtomicInteger threadNumber = new AtomicInteger(1);
        private final String namePrefix;

        DefaultThreadFactory() {
            SecurityManager s = System.getSecurityManager();
            group = (s != null) ? s.getThreadGroup() :
                                  Thread.currentThread().getThreadGroup();
            namePrefix = "pool-" +
                          poolNumber.getAndIncrement() +
                         "-thread-";
        }

        public Thread newThread(Runnable r) {
            Thread t = new Thread(group, r,
                                  namePrefix + threadNumber.getAndIncrement(),
                                  0);
            if (t.isDaemon())
                t.setDaemon(false);
            if (t.getPriority() != Thread.NORM_PRIORITY)
                t.setPriority(Thread.NORM_PRIORITY);
            return t;
        }
    }

移除一个工作线程

    private void addWorkerFailed(Worker w) {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            if (w != null)
            	//从 workers 中移除
                workers.remove(w);
            //循环CAS 减少工作线程数量
            decrementWorkerCount();
            //尝试切换到终止状态,之后具体分析
            tryTerminate();
        } finally {
            mainLock.unlock();
        }
    }

从阻塞队列中移除一个任务

    public boolean remove(Runnable task) {
        boolean removed = workQueue.remove(task);
        //这里也调用了
        tryTerminate(); // In case SHUTDOWN and now empty
        return removed;
    }

到这里有没有疑惑,工作线程是在什么时候执行任务的呢?
在 addWorker 方法中,增加工作线程到队列中成功,就启动此线程

w = new Worker(firstTask);
Thread t = w.thread;
...
if (workerAdded) {
    t.start();
    workerStarted = true;
}

Worker 既继承了 AbstractQueuedSynchronizer 也实现了 Runnable 接口

private final class Worker
    extends AbstractQueuedSynchronizer
    implements Runnable  {
    /** Delegates main run loop to outer runWorker  */
    public void run() {
        runWorker(this);
    }
    ...
    protected boolean tryRelease(int unused) {
        setExclusiveOwnerThread(null);
        setState(0);
        return true;
    }

}

工作线程又是怎么执行任务的呢?

final void runWorker(Worker w) {
    Thread wt = Thread.currentThread();
    Runnable task = w.firstTask;
    //清空 firstTask 
    w.firstTask = null;
    //这里会调用 tryRelease 方法,将自己的状态改为 0,并清除独占线程
    w.unlock(); // allow interrupts
    boolean completedAbruptly = true;
    try {
    	//最开始start一个线程的时候 firstTask 不会为空,之后需要从队列中取执行任务
        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
            //确保线程池关闭的情况下,给线程设置一个中断标识
            //此处是为了响应 shutdownNow 方法
            if ((runStateAtLeast(ctl.get(), STOP) ||
                 (Thread.interrupted() &&
                  runStateAtLeast(ctl.get(), STOP))) &&
                !wt.isInterrupted())
                wt.interrupt();
            try {
            	//钩子方法,在执行任务前执行
                beforeExecute(wt, task);
                Throwable thrown = null;
                try {
                	//真正开始执行任务的 run 方法,实际执行的是 FutureTask.run
                    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 {
    	//处理工作线程退出的情况
    	//可能获取不到任务后返回null,也可能是执行任务时超时、被中断、异常等
        processWorkerExit(w, completedAbruptly);
    }
}

如何确定工作线程是否要移除,以及如何从队列中取执行任务?

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())) {
        	//线程池调用了 shutdown 且队列没任务,或者调用了 shutdownNow,需要减少工作线程数
        	//并返回空,方便本线程退出
            decrementWorkerCount();
            return null;
        }

        int wc = workerCountOf(c);

        // Are workers subject to culling?
        //表明线程可能需要被移除
        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
		
		//线程数量大于设置的最大线程数或者线程超时了需要减少工作线程数
		//如果剩余线程数>1或者队列为空了,减少线程数
		//(队列不为空的情况下,确保线程池里面最少有一个线程)
        if ((wc > maximumPoolSize || (timed && timedOut))
            && (wc > 1 || workQueue.isEmpty())) {
            if (compareAndDecrementWorkerCount(c))
                return null;
            continue;
        }

        try {
        	//从队列中取元素,如果线程数量超出了核心线程数或者允许核心线程退出,调用 poll 等待一段时间后退出
        	//若不允许线程退出,则调用 take 阻塞
            Runnable r = timed ?
                workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                workQueue.take();
            if (r != null)
            	//从阻塞队列中获取到元素后返回任务
                return r;
            //poll 自动唤醒了,说明超时,再次循环判断是否需要移除
            timedOut = true;
        } catch (InterruptedException retry) {
            timedOut = false;
        }
    }
}

处理工作线程退出的情况

private void processWorkerExit(Worker w, boolean completedAbruptly) {
	//获取不到任务,completedAbruptly 为 false,工作线程总数已经减一了,不用调整
    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();
   	//线程池没有调用 shutdownNow 的情况下
    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);
    }
}

关闭线程池会执行什么操作,shutdown 和 shutdownNow 的区别是怎么实现的?

public void shutdown() {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        checkShutdownAccess();
        //修改线程池状态为 SHUTDOWN
        advanceRunState(SHUTDOWN);
       	//只中断空闲工作线程
        interruptIdleWorkers();
        onShutdown(); // hook for ScheduledThreadPoolExecutor
    } finally {
        mainLock.unlock();
    }
    tryTerminate();
}
private void interruptIdleWorkers(boolean onlyOne) {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        for (Worker w : workers) {
            Thread t = w.thread;
           	//线程没有被中断,且获取到锁了才执行设置中断操作
            if (!t.isInterrupted() && w.tryLock()) {
                try {
                    t.interrupt();
                } catch (SecurityException ignore) {
                } finally {
                    w.unlock();
                }
            }
            if (onlyOne)
                break;
        }
    } finally {
        mainLock.unlock();
    }
}

public List<Runnable> shutdownNow() {
    List<Runnable> tasks;
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        checkShutdownAccess();
        //修改线程池状态为 STOP
        advanceRunState(STOP);
        //中断所有工作线程
        interruptWorkers();
        //将阻塞队列中的任务移除并增加到 tasks 中
        tasks = drainQueue();
    } finally {
        mainLock.unlock();
    }
    tryTerminate();
    return tasks;
}
private void interruptWorkers() {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        for (Worker w : workers)
            w.interruptIfStarted();
    } finally {
        mainLock.unlock();
    }
}
void interruptIfStarted() {
    Thread t;
    //只要启动了,就执行设置中断操作
    if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
        try {
            t.interrupt();
        } catch (SecurityException ignore) {
        }
    }
}

如何区分线程是否空闲?看 runWorker 方法内的循环,执行任务前要先获取工作线程的锁 w.lock() ,能获取到锁,说明工作线程是空闲的。线程没启动线程池的关闭方法是中断不了线程的,runWorker 开头的 w.unlock() 会将工作线程的 state 设置为0。

shutdown 只设置了线程池状态为 SHUTDOWN,并设置了空闲线程的中断标志没有上锁就是空闲的;
shutdownNow 设置了线程池状态为 STOP,并设置了所有启动了的线程的中断标志;他们都调用了 tryTerminate 方法。

tryTerminate 用于尝试将线程池状态切换成终止状态,需满足这两种情况:1.调用了 shutdown 且线程数量和队列都为空;2.调用了 shutdownNow 并且线程数量为空。
增加线程失败,线程退出,从队列中移除任务,或者关闭线程池都会调用此方法。

/**
 * Transitions to TERMINATED state if either (SHUTDOWN and pool
 * and queue empty) or (STOP and pool empty).  If otherwise
 * eligible to terminate but workerCount is nonzero, interrupts an
 * idle worker to ensure that shutdown signals propagate. This
 * method must be called following any action that might make
 * termination possible -- reducing worker count or removing tasks
 * from the queue during shutdown. The method is non-private to
 * allow access from ScheduledThreadPoolExecutor.
 */
final void tryTerminate() {
    for (;;) {
        int c = ctl.get();
        if (isRunning(c) ||
            runStateAtLeast(c, TIDYING) ||
            (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
            return;
        //还存在工作线程,就关闭空闲线程,保证能传递 shutdown 信号
        if (workerCountOf(c) != 0) { // Eligible to terminate
            interruptIdleWorkers(ONLY_ONE);
            return;
        }

        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
        	//设置完全终止状态分两步,先设置成 TIDYING 状态
            if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
                try {
                    terminated();
                } finally {
                	//再设置成 TERMINATED 状态
                    ctl.set(ctlOf(TERMINATED, 0));
                    termination.signalAll();
                }
                return;
            }
        } finally {
            mainLock.unlock();
        }
        // else retry on failed CAS
    }
}

terminated 当线程池被完全终止后,需要调用的方法,默认为空。类似的可扩展方法还有:在调用任务之前执行 beforeExecute,在调用任务之后执行 afterExecute。这些方法可以对线程池进行监控。

提交一个任务到线程池后,如何获取任务的执行的结果呢?
submit 方法会返回一个 Future 接口,调用 Future.get 就能获取到结果,如果任务还么有执行完就阻塞,这是怎么做到的呢,看下篇文章 FutureTask详解

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值