理解线程池原理

前言
线程池在我们代码里面使用越来越频繁,相对于单线程的执行,优势在于维护了一个线程池,进行对线程的管理,尽可能减少线程的创建与销毁。因为线程的创建与销毁很占用cpu的资源。

## 线程池的基本使用

public class ThreadPoolFactory {
    private static volatile ExecutorService pool;

    private ThreadPoolFactory() {
    }

    public static ExecutorService getExecutorService() {
        if (pool == null) {
            synchronized (ThreadPoolFactory.class) {
                if (pool == null) {
                    ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
                            .setNameFormat("threadPoolFactory--%d").build();
                    // 获取处理器数量
                    int cpuNum = Runtime.getRuntime().availableProcessors();
                    // 根据cpu数量,计算出合理的线程并发数
                    int threadNum = cpuNum * 2 + 1;
                    //Common Thread Pool
                    pool = new ThreadPoolExecutor(threadNum, threadNum,
                            0L, TimeUnit.MILLISECONDS,
                            new LinkedBlockingQueue<>(1024)
                            , namedThreadFactory
                            , new ThreadPoolExecutor.AbortPolicy());
                }
            }
        }
        return pool;
    }

}

下面解释下一下构造器中各个参数的含义:
corePoolSize:核心线程数。默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中;
maximumPoolSize:最大线程数。
keepAliveTime:表示线程没有任务执行时最多保持多久时间会终止。默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用,直到线程池中的线程数不大于corePoolSize,即当线程池中的线程数大于corePoolSize时,如果一个线程空闲的时间达到keepAliveTime,则会终止,直到线程池中的线程数不超过corePoolSize。但是如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0;
workQueue:一个阻塞队列,用来存储等待执行的任务,这个参数的选择也很重要,会对线程池的运行过程产生重大影响,一般来说,这里的阻塞队列有以下几种选择:

ArrayBlockingQueue;//数组式队列
LinkedBlockingQueue;//
SynchronousQueue;//抽象式队列

ArrayBlockingQueue:列表形式的工作队列,必须要有初始队列大小,有界队列,先进先出。
LinkedBlockingQueue:链表形式的工作队列,可以选择设置初始队列大小,有界/无界队列,先进先出。
SynchronousQueue:SynchronousQueue不是一个真正的队列,而是一种在线程之间移交的机制。要将一个元素放入SynchronousQueue中, 必须有另一个线程正在等待接受这个元素. 如果没有线程等待,并且线程池的当前大小小于最大值,那么ThreadPoolExecutor将创建 一个线程, 否则根据饱和策略,这个任务将被拒绝。使用直接移交将更高效,因为任务会直接移交 给执行它的线程,而不是被首先放在队列中, 然后由工作者线程从队列中提取任务. 只有当线程池是无解的或者可以拒绝任务时,SynchronousQueue才有实际价值.

threadFactory:线程工厂,主要用来创建线程;
handler:表示当拒绝处理任务时的策略,有以下四种取值:

ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。 
ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。 
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务 

ThreadPoolExecutor的继承关系

ThreadPoolExecutor继承抽象AbstractExecutorService,AbstractExecutorService实现ExecutorService,ExecutorService继承Executor。

ThreadPoolExecutor源码解析
先了解线程的状态,有利于理解线程池原理。
下面的源码解读中提到的运行状态就是runState,有效的线程数就是workerCount,内容比较多,所以可能两种写法都用到。
运行状态的一些定义:RUNNING:接受新任务并处理排队任务; SHUTDOWN:不接受新任务,但处理排队任务; STOP:不接受新任务,不处理排队任务,并中断正在进行的任务;TIDYING:所有任务已经终止,workerCount为零,线程转换到状态TIDYING将运行terminate()钩子方法;TERMINATED:terminated()已经完成,该方法执行完毕代表线程池已经完全终止。
运行状态之间并不是随意转换的,大多数状态都只能由固定的状态转换而来,转换关系见第4点~第8点。
RUNNING - > SHUTDOWN:在调用shutdown()时,可能隐含在finalize()。
(RUNNING or SHUTDOWN) -> STOP:调用shutdownNow()。
SHUTDOWN - > TIDYING:当队列和线程池都是空的时。
STOP - > TIDYING:当线程池为空时。
TIDYING - > TERMINATED:当terminate()方法完成时

/**
 * 主池控制状态ctl是包含两个概念字段的原子整数: workerCount:指有效的线程数量;
 * runState:指运行状态,运行,关闭等。为了将workerCount和runState用1个int来表示,
 * 我们限制workerCount范围为(2 ^ 29) - 1,即用int的低29位用来表示workerCount,
 * 用int的高3位用来表示runState,这样workerCount和runState刚好用int可以完整表示。
 */
// 初始化时有效的线程数为0, 此时ctl为: 1010 0000 0000 0000 0000 0000 0000 0000 
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)); 
// 高3位用来表示运行状态,此值用于运行状态向左移动的位数,即29位
private static final int COUNT_BITS = Integer.SIZE - 3;     
// 线程数容量,低29位表示有效的线程数, 0001 1111 1111 1111 1111 1111 1111 1111
private static final int CAPACITY   = (1 << COUNT_BITS) - 1;
 
/**
 * 大小关系:RUNNING < SHUTDOWN < STOP < TIDYING < TERMINATED,
 * 源码中频繁使用大小关系来作为条件判断。
 * 1010 0000 0000 0000 0000 0000 0000 0000 运行
 * 0110 0000 0000 0000 0000 0000 0000 0000 关闭
 * 0110 0000 0000 0000 0000 0000 0000 0000 停止
 * 0110 0000 0000 0000 0000 0000 0000 0000 整理
 * 0110 0000 0000 0000 0000 0000 0000 0000 终止
 */
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; // 终止
 
/**
 * 得到运行状态:入参c为ctl的值,~CAPACITY高3位为1低29位全为0, 
 * 因此运算结果为ctl的高3位, 也就是运行状态
 */
private static int runStateOf(int c)     { return c & ~CAPACITY; }  
/**
 * 得到有效的线程数:入参c为ctl的值, CAPACITY高3为为0, 
 * 低29位全为1, 因此运算结果为ctl的低29位, 也就是有效的线程数
 */
private static int workerCountOf(int c)  { return c & CAPACITY; }   
/**
 * 得到ctl的值:高3位的运行状态和低29位的有效线程数进行或运算, 
 * 组合成一个完成的32位数
 */
private static int ctlOf(int rs, int wc) { return rs | wc; }    
 
// 状态c是否小于s
private static boolean runStateLessThan(int c, int s) { 
    return c < s;
}
// 状态c是否大于等于s
private static boolean runStateAtLeast(int c, int s) {
    return c >= s;
}
// 状态c是否为RUNNING(小于SHUTDOWN的状态只有RUNNING)
private static boolean isRunning(int c) {
    return c < SHUTDOWN;
}
 
// 使用CAS增加一个有效的线程
private boolean compareAndIncrementWorkerCount(int expect) {    
    return ctl.compareAndSet(expect, expect + 1);
}
 
// 使用CAS减少一个有效的线程
private boolean compareAndDecrementWorkerCount(int expect) {    
    return ctl.compareAndSet(expect, expect - 1);
}
 
// 减少一个有效的线程
private void decrementWorkerCount() {
    do {} while (! compareAndDecrementWorkerCount(ctl.get()));
}
 
// 工作队列
private final BlockingQueue<Runnable> workQueue;    
 
// 锁
private final ReentrantLock mainLock = new ReentrantLock(); 
 
// 包含线程池中的所有工作线程,只有在mainLock的情况下才能访问,Worker集合
private final HashSet<Worker> workers = new HashSet<Worker>();
 
private final Condition termination = mainLock.newCondition();
 
// 跟踪线程池的最大到达大小,仅在mainLock下访问
private int largestPoolSize;
 
// 总的完成的任务数
private long completedTaskCount;
 
// 线程工厂,用于创建线程
private volatile ThreadFactory threadFactory;
 
// 拒绝策略
private volatile RejectedExecutionHandler handler;
 
 
/**
 * 线程超时时间,当线程数超过corePoolSize时生效, 
 * 如果有线程空闲时间超过keepAliveTime, 则会被终止
 */
private volatile long keepAliveTime;    
 
// 是否允许核心线程超时,默认false,false情况下核心线程会一直存活。
private volatile boolean allowCoreThreadTimeOut;
 
// 核心线程数
private volatile int corePoolSize;
 
// 最大线程数
private volatile int maximumPoolSize;
 
// 默认饱和策略(拒绝策略), 抛异常
private static final RejectedExecutionHandler defaultHandler = 
    new AbortPolicy();
 
private static final RuntimePermission shutdownPerm =
    new RuntimePermission("modifyThread");

线程中的worker源码理解:

/**
 * Worker类,每个Worker包含一个线程、一个初始任务、一个任务计算器
 */
private final class Worker   
    extends AbstractQueuedSynchronizer
    implements Runnable
{
    private static final long serialVersionUID = 6138294804551838833L;
 
    final Thread thread;    // Worker对应的线程
    Runnable firstTask; // 运行的初始任务。
    volatile long completedTasks;   // 每个线程的任务计数器
 
    Worker(Runnable firstTask) {
        setState(-1); // 禁止中断,直到runWorker
        this.firstTask = firstTask; // 设置为初始任务
        // 使用当前线程池的线程工厂创建一个线程
        this.thread = getThreadFactory().newThread(this);  
    }
 
    // 将主运行循环委托给外部runWorker
    public void run() {
        runWorker(this);
    }
 
    // Lock methods
    //
    // The value 0 represents the unlocked state.
    // The value 1 represents the locked state.
    /**
     * 通过AQS的同步状态来实现锁机制。state为0时代表锁未被获取(解锁状态),
     * state为1时代表锁已经被获取(加锁状态)。
     */
    protected boolean isHeldExclusively() { // 
        return getState() != 0; 
    }
    protected boolean tryAcquire(int unused) {  // 尝试获取锁
        if (compareAndSetState(0, 1)) { // 使用CAS尝试将state设置为1,即尝试获取锁
            // 成功将state设置为1,则当前线程拥有独占访问权
            setExclusiveOwnerThread(Thread.currentThread());    
            return true;
        }
        return false;
    }
    protected boolean tryRelease(int unused) {  // 尝试释放锁
        setExclusiveOwnerThread(null);  // 释放独占访问权:即将独占访问线程设为null
        setState(0);    // 解锁:将state设置为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) {
            }
        }
    }
}

execute方法
使用线程池的submit方法提交任务时,会走到该方法,该方法也是线程池最重要的方法。

public void execute(Runnable command) {
		//任务为空  空指针返回
        if (command == null)
            throw new NullPointerException();
        int c = ctl.get();
        //当前线程数小于核心线程数
        if (workerCountOf(c) < corePoolSize) {
        //当前线程池小于核心线程,增加核心线程执行任务  
     	//addWorker的true表示给核心线程分配任务  false表示给非核心线程分配任务
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        //当前线程数大于核心线程数   线程池处于running状态才会接受任务否则队列不接受新任务 
        //且添加队列成功
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            //添加队列成功,继续判断线程池状态 线程池等于running状态,且移除队列成功
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        //我对worker的理解就是把任务Runnable分配给线程,工作任务指定人  
        //任务分配失败,直接拒绝任务
        else if (!addWorker(command, false))
            reject(command);
    }

该方法就是对应上文的线程池的工作流程。主要调用到的方法为addWorker(见下文addWorker方法解读)。

addWorker方法

/**
 * 添加一个Worker,Worker包含一个线程和一个任务,由这个线程来执行该任务。
 */
private boolean addWorker(Runnable firstTask, boolean core) {   
    retry:
    for (;;) {
        int c = ctl.get();  // c赋值为ctl
        int rs = runStateOf(c); // rs赋值为运行状态
        /**
         * 1.如果池停止或有资格关闭,则此方法返回false;
         * 如果线程工厂在被询问时未能创建线程,它也返回false。 
         * 包括以下5种情况:
         * 1).rs为RUNNING,通过校验。
         * 2).rs为STOP或TIDYING或TERMINATED,返回false。
         * (STOP、TIDYING、TERMINATED:已经停止进入最后清理终止,不接受任务不处理队列任务)
         * 3).rs为SHUTDOWN,提交的任务不为空,返回false。
         * (SHUTDOWN:不接受任务但是处理队列任务,因此任务不为空返回false)
         * 4).rs为SHUTDOWN,提交的任务为空,并且工作队列为空,返回false。
         * (状态为SHUTDOWN、提交的任务为空、工作队列为空,则线程池有资格关闭,直接返回false)
         * 5).rs为SHUTDOWN,提交的任务为空,并且工作队列不为空,通过校验。
         * (因为SHUTDOWN状态下刚好可以处理队列任务)
         */
        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将workerCount+1, 修改成功则跳出循环,否则进入下面的状态判断
            if (compareAndIncrementWorkerCount(c))
                break retry;
            c = ctl.get();  // 重新读取ctl
            // 判断当前运行状态,如果不等于上面获取的运行状态rs,
            // 说明rs被其他线程修改了,跳到retry重新校验线程池状态
            if (runStateOf(c) != rs)
                continue retry;
            // 走到这里说明compareAndIncrementWorkerCount失败; 
            // 重试内部循环(状态没变,则继续内部循环,尝试使用CAS修改workerCount)
        }
    }
 
    boolean workerStarted = false;  // Worker的线程是否启动
    boolean workerAdded = false;    // Worker是否成功增加
    Worker w = null;
    try {
        w = new Worker(firstTask);  // 用firstTask和当前线程创建一个Worker
        final Thread t = w.thread;  // 拿到Worker对应的线程
        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()); // 加锁的情况下重新获取当前的运行状态
 
                // 如果当前的运行状态为RUNNING,
                // 或者当前的运行状态为SHUTDOWN并且firstTask为空,则通过校验
                if (rs < SHUTDOWN ||
                    (rs == SHUTDOWN && firstTask == null)) {
                    if (t.isAlive())    // 预先校验线程是可以启动的
                        throw new IllegalThreadStateException();
                    workers.add(w); // 将刚创建的worker添加到工作者列表
                    int s = workers.size();
                    if (s > largestPoolSize)
                        largestPoolSize = s;
                    workerAdded = true;
                }
            } finally {
                mainLock.unlock();
            }
            if (workerAdded) {  // 如果Worker添加成功,则启动线程执行
                t.start();
                workerStarted = true;
            }
        }
    } finally {
        if (! workerStarted)    // 如果Worker的线程没有成功启动
            addWorkerFailed(w); // 则进行回滚, 移除之前添加的Worker
    }
    return workerStarted;
}

runWorker方法
上文addWork方法里说道,当Worker里的线程启动时,就会调用该方法。

/**
 * Worker的线程开始执行任务
 */
final void runWorker(Worker w) {
    Thread wt = Thread.currentThread(); // 获取当前线程
    Runnable task = w.firstTask;    // 拿到Worker的初始任务
    w.firstTask = null;
    w.unlock(); // allow interrupts
    boolean completedAbruptly = true;   // Worker是不是因异常而死亡
    try {
        while (task != null || (task = getTask()) != null) {// Worker取任务执行
            w.lock();   // 加锁
            /**如果线程池停止,确保线程中断; 如果不是,确保线程不被中断。
             * 在第二种情况下进行重新检查,以便在清除中断的同时处理shutdownNow竞争
             * 线程池停止指运行状态为STOP/TIDYING/TERMINATED中的一种
             */
            if ((runStateAtLeast(ctl.get(), STOP) ||    // 判断线程池运行状态
                 (Thread.interrupted() &&   // 重新检查
                  runStateAtLeast(ctl.get(), STOP))) && // 再次判断线程池运行状态
                !wt.isInterrupted())// 走到这里代表线程池运行状态为停止,检查wt是否中断
                wt.interrupt(); // 线程池的状态为停止并且wt不为中断, 则将wt中断
            try {
                beforeExecute(wt, task);// 执行beforeExecute(默认空,需要自己重写)
                Throwable thrown = null;
                try {
                    task.run(); // 执行任务
                } catch (RuntimeException x) {
                    thrown = x; throw x; //如果抛异常,则completedAbruptly为true
                } catch (Error x) {
                    thrown = x; throw x;
                } catch (Throwable x) {
                    thrown = x; throw new Error(x);
                } finally {
                    afterExecute(task, thrown);// 执行afterExecute(需要自己重写)
                }
            } finally {
                task = null;    // 将执行完的任务清空
                w.completedTasks++; // Worker完成任务数+1
                w.unlock();
            }
        }
        completedAbruptly = false;  // 如果执行到这里,则worker是正常退出
    } finally {
        processWorkerExit(w, completedAbruptly);// 调用processWorkerExit方法
    }
}

该方法为Worker线程开始执行任务,首先执行当初创建Worker时的初始任务,接着从工作队列中获取任务执行。主要涉及两个方法:获取任务的方法getTask(见下文getTask源码解读)和执行Worker退出的方法processWorkerExit(见下文processWorkerExit源码解读)。注:processWorkerExit在处理正常Worker退出时,没有对workerCount-1,而是在getTask方法中进行workerCount-1。

getTask方法

private Runnable getTask() {    // Worker从工作队列获取任务
    boolean timedOut = false; // poll方法取任务是否超时
 
    for (;;) {  // 无线循环
        int c = ctl.get();  // ctl
        int rs = runStateOf(c); // 当前运行状态
 
        // 如果线程池运行状态为停止,或者可以停止(状态为SHUTDOWN并且队列为空)
        // 则返回null,代表当前Worker需要移除
        if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {    
            decrementWorkerCount(); // 将workerCount - 1
            // 返回null前将workerCount - 1,
            // 因此processWorkerExit中completedAbruptly=false时无需再减
            return null;
        }
 
        int wc = workerCountOf(c);  // 当前的workerCount
 
        // 判断当前Worker是否可以被移除, 即当前Worker是否可以一直等待任务。
        // 如果allowCoreThreadTimeOut为true,或者workerCount大于核心线程数,
        // 则当前线程是有超时时间的(keepAliveTime),无法一直等待任务。
        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;    
 
        // 如果wc超过最大线程数 或者 当前线程会超时并且已经超时,
        // 并且wc > 1 或者 工作队列为空,则返回null,代表当前Worker需要移除
        if ((wc > maximumPoolSize || (timed && timedOut))
            && (wc > 1 || workQueue.isEmpty())) {   // 确保有Worker可以移除 
            if (compareAndDecrementWorkerCount(c))
                // 返回null前将workerCount - 1,
                // 因此processWorkerExit中completedAbruptly=false时无需再减
                return null;    
            continue;
        }
 
        try {
            // 根据线程是否会超时调用相应的方法,poll为带超时的获取任务方法
            // take()为不带超时的获取任务方法,会一直阻塞直到获取到任务
            Runnable r = timed ? 
                workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                workQueue.take();
            if (r != null)
                return r;
            timedOut = true;    // 走到这代表当前线程获取任务超时
        } catch (InterruptedException retry) {
            timedOut = false;   // 被中断
        }
    }
}

Worker从工作队列获取任务,如果allowCoreThreadTimeOut为false并且 workerCount<=corePoolSize,则这些核心线程永远存活,并且一直在尝试获取工作队列的任务;否则,线程会有超时时间(keepAliveTime),当在keepAliveTime时间内获取不到任务,该线程的Worker会被移除。
Worker移除的过程:getTask方法返回null,导致runWorker方法中跳出while循环,调用processWorkerExit方法将Worker移除。注意:在返回null的之前,已经将workerCount-1,因此在processWorkerExit中,completedAbruptly=false的情况(即正常超时退出)不需要再将workerCount-1。
参考:
参考文章:https://blog.csdn.net/v123411739/article/details/79124193
参考文章:https://blog.csdn.net/gol_phing/article/details/49032055

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值