Java并发-ThreadPoolExecutor

线程池

线程池与连接池的概念非常相似,所谓线程池就是在一个容器中装着提前准备好的线程,在我们需要用到线程的时候可以直接从池子中获取,使用完之后返回容器中。

线程池解决的主要问题

  1. 执行大量异步任务时线程池能够提供较好的性能,如果不使用线程池,每次去新建和销毁线程都需要额外的开销。
  2. 线程池可以进行资源限制和管理,比如可以限制线程的个数,动态新增线程等。
  3. 根据不同的场景进行参数的定制化可以适应不同的场景要求。

ctl

ctl是一个原子整形AtomicInteger,它用来表示线程池状态和线程池中线程的个数,其中高3位用来表示线程池状态,后面其他位数(如果Integer是32位的,则是29位)用来记录线程池线程个数。

//(高3位)用来表示线程池状态,(低29位)用来表示线程个数
//默认是RUNNING状态,线程个数为0
private final atomicInteger ctl = new AtomicInteger(ctlOf(RUNNING,0));
//线程个数掩码位数,并不是所有平台的int类型都是32位的,所以准确地说,是具体平台下Integer的二进制位数-3后的剩余位数所表示的数才是线程的个数
private static final int COUNT_BITS = Integer.SIZE - 3;
//线程最大个数(低29位)00011111111111111111111111111111
private static final int CAPACITY = (l << COUNT_BITS)- 1;

线程池的状态

//(高3位): 11100000000000000000000000000000
private static final int RUNNING= -l << COUNT_BITS;
//(高3位): 00000000000000000000000000000000
private static final int SHUTDON = 0 << COUNT_BITS;
//(高3位): 00100000000000000000000000000000
private static final int STOP = -l << COUNT_BITS;
//(高3位): 010000000000000000000000000000o0
private static final int TIDYING= 2 << COUNT_BITS;
//(高3位) : 01100000000o00000000000000000000
private static final int TERMINATED = 3 << COUNT_BITS;
  • RUNNING:接受新任务并且处理阻塞队列里的任务。
  • SHUTDOWN:拒绝新任务但是处理阻塞队列里的任务。
  • STOP:拒绝新任务并且抛弃阻塞队列里的任务,同时会中断正在处理的任务。
  • TIDYING:所有任务都执行完(包含阻塞队列里面的任务)后当前线程池活动线程数为0,将要调用terminated方法。
  • TERMINATED:终止状态。terminated方法调用完成以后的状态。

线程池状态的变化

RUNNING -> SHUTDOWN:显式调用shutdown()方法,或者隐式调用了finalize()方法里面的shutdown()方法。
RUNNING或SHUTDOWN) -> STOP:显式调用shutdownNow()方法时。SHUTDOWN -> TIDYING:当线程池和任务队列都为空时。
STOP -> TIDYING:当线程池为空时。
TIDYING -> TERMINATED:当terminated() hook方法执行完成时。

线程池核心参数

corePoolSize:线程池核心线程个数。
workQueue:用于保存等待执行的任务的阻塞队列,比如基于数组的有界ArrayBlockingQueue、基于链表的无界LinkedBlockingQueue、最多只有一个元素的同步队列SynchronousQueue及优先级队列 PriorityBlockingQueue等。
maximunPoolSize:线程池最大线程数量。
ThreadFactory:创建线程的工厂。
RejectedExecutionHandler:饱和策略,当队列满并且线程个数达到maximunPoolSize后采取的策略,比如 AbortPolicy (抛出异常)、CallerRunsPolicy(使用调用者所在线程来运行任务)、DiscardOldestPolicy (调用poll丢弃一个任务,执行当前任务)及DiscardPolicy(默默丢弃﹐不抛出异常)
keeyAliveTime:存活时间。如果当前线程池中的线程数量比核心线程数量多,并且是闲置状态,则这些闲置的线程能存活的最大时间。
TimeUnit :存活时间的时间单位。

线程池运行的基本原理

当我们使用线程池去execute一个任务的时候,线程池会发生以下步骤:

  1. 当ctl记录的当前线程数量少于corePoolSize的大小时,就会添加一个新的核心线程。
  2. 当ctl记录的当前线程数量等于corePoolSize的大小时,就会把当前任务放到阻塞队列workQueue中。
  3. 当workQueue满了,会判断ctl记录的当前线程数量是否小于maximunPoolSize的大小,如果是,则会创建非核心线程来执行任务。
  4. 以上操作都没有成功,则会执行拒绝策略。

线程池的解决策略

  1. ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
  2. ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常。如果线程队列已满,则后续提交的任务都会被丢弃,且是静默丢弃。
  3. ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新提交被拒绝的任务。
  4. ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务。

源码分析

获得运行状态和当前线程数

// 获取高3位(运行状态)
private static int runStateOf(int c){ return c & ~CAPACITY; }
// 获得当前线程数量
private static int workerCountOf(int c)  { return c & CAPACITY; }

public void execute(Runnable command)

public void execute(Runnable command) {
    // 当前任务为空,直接报错
    if (command == null)
        throw new NullPointerException();
    // 获得ctl
    int c = ctl.get();
    // 如果当前运行的线程数量小于corePoolSize,执行以下逻辑
    if (workerCountOf(c) < corePoolSize) {
        // 通过addWorker方法,增加一个线程
        if (addWorker(command, true))
            // 增加成功直接返回
            return;
        // 运行到这里证明没有添加成功,addWorker方法通过CAS来保证安全性,addWorker失败说明可能线程池运行状态可能变化了或者有线程抢先添加了一个线程,无论如何,这时候ctl的值肯定已经变化了,所以获得最新的ctl值。
        c = ctl.get();
    }
    // 用isRunning方法判断线程是否还是RUNNING状态,如果是,说明之前addWorker方法添加失败是因为已经等于corePoolSize,所以调用workQueue.offer把任务添加到阻塞队列,成功返回true,失败返回false。
    if (isRunning(c) && workQueue.offer(command)) {
        // 添加到阻塞队列中要重新获取一下ctl,进行运行状态的判断,因为在这期间有可能线程池被停止了,那么就要把任务移除,同时执行拒绝策略。
        int recheck = ctl.get();
        if (! isRunning(recheck) && remove(command))
            // 执行拒绝策略
            reject(command);
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false);
    }
    // 可以跑到这一步说明,当前线程数等于corePoolSize,阻塞队列也是满的,那么就调用addWorker方法去添加非核心线程,如果添加失败,证明当前线程数量已经等于maximunPoolSize的大小了,就执行拒绝策略。
    else if (!addWorker(command, false))
        reject(command);
}

private boolean addWorker(Runnable firstTask, boolean core)

参数firstTask是默认任务,core为true时,表示创建的是核心线程,core为false时,表示创建的是非核心线程。

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

        // 检查队列是否只在必要的时候为空
        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;
             // CAS去修改当前的线程数量
            if (compareAndIncrementWorkerCount(c))
                // 修改成功就跳出双重循环
                break retry;
            c = ctl.get();
            // 如果线程池的运行状态没有发生变化,则继续循环
            if (runStateOf(c) != rs)
                continue retry;
        }
    }
    // 可以跑到这里,说明已经跳出了循环,也就是CAS已经成功了
    boolean workerStarted = false;
    boolean workerAdded = false;
    Worker w = null;
    try {
        // 新建一个worker
        w = new Worker(firstTask);
        final Thread t = w.thread;
        if (t != null) {
            // 这里有一个全局的锁,是为了保证对worker集合的操作都是线程安全的
            final ReentrantLock mainLock = this.mainLock;
            mainLock.lock();
            try {
                int rs = runStateOf(ctl.get());

                if (rs < SHUTDOWN ||
                    (rs == SHUTDOWN && firstTask == null)) {
                    if (t.isAlive()) // precheck that t is startable
                        throw new IllegalThreadStateException();
                    // 把新建的worker加到集合中(HashSet)
                    workers.add(w);
                    int s = workers.size();
                    if (s > largestPoolSize)
                        largestPoolSize = s;
                    workerAdded = true;
                }
            } finally {
                mainLock.unlock();
            }
            if (workerAdded) {
                // 启动worker线程
                t.start();
                workerStarted = true;
            }
        }
    } finally {
        if (! workerStarted)
            addWorkerFailed(w);
    }
    return workerStarted;
}

工作线程Worker

Worker的构造函数如下:

Worker(Runnable firstTask) {
    // 这里把Worker的state设置为-1,处于-1的worker说明正在创建,就算是shutdownNow也不会中断它。
    setState(-1);
    this.firstTask = firstTask;
    // 通过线程工厂抽检一个线程
    this.thread = getThreadFactory().newThread(this);
}

启动worker,最终会调用到runWorker方法:

public void run() {
    runWorker(this);
}

final void runWorker(Worker w) {
    Thread wt = Thread.currentThread();
    Runnable task = w.firstTask;
    w.firstTask = null;
    // 这个方法会把state设置为0,当state >= 0时,shutdownNow就可以中断这些线程了
    w.unlock();
    boolean completedAbruptly = true;
    try {
        // getTask从队列中获得任务,如果返回的是null,表示这个线程应该被回收了(非核心线程过期,或者设置的核心线程过期)
        while (task != null || (task = getTask()) != null) {
            // worker本身就实现了AQS,worker运行的时候会加锁,因为shutdown的时候要获取每一个worker的锁才会中断,这样可以保证在运行的线程不会被中断,而shutdownNow不会拿锁,会直接中断线程。
            w.lock();
            // 如果线程池是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;
                w.completedTasks++;
                w.unlock();
            }
        }
        completedAbruptly = false;
    } finally {
        // 如果非核心线程过期,或者设置的核心线程过期,进行最后的一些处理,包括把worker从集合移除等操作(如果线程池terminated,会通知所有调用了termination.await的线程)
        processWorkerExit(w, completedAbruptly);
    }
}

private Runnable getTask() {
    boolean timedOut = false;

    for (;;) {
        // 基本的做一些检查
        int c = ctl.get();
        int rs = runStateOf(c);

        if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
            decrementWorkerCount();
            return null;
        }

        // 当前的线程数量
        int wc = workerCountOf(c);

        // allowCoreThreadTimeOut如果是false(默认),就是运行核心线程即使在空闲时也保持活动。
        // allowCoreThreadTimeOut如果是true,核心线程也会加入到过期判断。
        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

        // 空闲的非核心线程会在这里被回收,返回null
        if ((wc > maximumPoolSize || (timed && timedOut))
                        && (wc > 1 || workQueue.isEmpty())) {
                        if (compareAndDecrementWorkerCount(c))
                            return null;
                        continue;
                    }

        try {
            // 如果核心线程也要加入到过期判断,或者当前线程数大于corePoolSize,就用带超时时间的poll方法,如果超时会返回null,那么就证明线程超时了,可以直接回收线程。take会阻塞,等到有任务。
            // 这里就是非核心线程超时消除的原理
            Runnable r = timed ?
                workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                workQueue.take();
            if (r != null)
                return r;
            timedOut = true;
        } catch (InterruptedException retry) {
            timedOut = false;
        }
    }
}

public void shutdown()

public void shutdown() {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        // 权限校验
        checkShutdownAccess();
        // 设置当前线程池状态为SHUTDOWN,如果已经是SHUTDOWN则直接返回
        advanceRunState(SHUTDOWN);
        interruptIdleWorkers();
        // 钩子函数
        onShutdown(); // hook for ScheduledThreadPoolExecutor
    } finally {
        mainLock.unlock();
    }
    tryTerminate();
}

private void interruptIdleWorkers() {
    interruptIdleWorkers(false);
}

// 参数表示是否只中断一个线程
private void interruptIdleWorkers(boolean onlyOne) {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        for (Worker w : workers) {
            Thread t = w.thread;
            // 线程不是中断状态,尝试去拿锁,前面说过worker运行的时候也会拿锁,也就是如果worker还在运行,这里就不会拿到锁,会直接跳过正在运行的worker
            if (!t.isInterrupted() && w.tryLock()) {
                try {
                    t.interrupt();
                } catch (SecurityException ignore) {
                } finally {
                    w.unlock();
                }
            }
            if (onlyOne)
                break;
        }
    } finally {
        mainLock.unlock();
    }
}

public List shutdownNow()

public List<Runnable> shutdownNow() {
    List<Runnable> tasks;
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        // 权限校验
        checkShutdownAccess();
        // 设置当前线程池状态为STOP,如果已经是STOP则直接返回
        advanceRunState(STOP);
        // 中断worker
        interruptWorkers();
        // 将队列任务移动到tasks中
        tasks = drainQueue();
    } finally {
        mainLock.unlock();
    }
    tryTerminate();
    return tasks;
}

private void interruptWorkers() {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        // 不需要获得worker的锁,只需要判断worker的state不是-1就可以进行中断
        for (Worker w : workers)
            w.interruptIfStarted();
    } finally {
        mainLock.unlock();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值