线程在线程池中是如何处理的?线程池中的线程是如何复用的?线程池中的参数有哪些?

线程池流程图

个人认为,别人画的流程图是不容易记忆或者容易忘记的东西;这里就不提供不太有价值的流程图了。个人建议花时间看一下源码设计,真正理解了代码在程序中是如何运转的,各个阶段各个分支是怎么处理的,自己再根据自己的理解画出自己认为正确的流程图,相信这样的流程图画出来之后更容易记忆也不容易忘记。

线程池参数

int corePoolSize:						// 核心线程数,核心工作线程的数量
int maximumPoolSize: 					// 最大线程数,包括核心线程数和非核心线程数
long keepAliveTime: 					// 非核心线程空闲存活时间
TimeUnit unit: 						// 非核心线程空闲存活时间单位,配合keepAliveTime使用
BlockingQueue<Runnable> workQueue:		// 阻塞队列,用于存放工作线程未来得及处理的待执行任务
ThreadFactory threadFactory:			// 线程工厂,用于生成worker对象中真正处理任务的线程。
RejectedExecutionHandler handler: 		// 拒绝策略,当工作线程数达到了最大线程数且队列中的待执行任务已满,会触发拒绝策略

线程池的各个参数,在源码中是如何使用的,可以带着问题去源码中寻找答案。

源码分析

线程池执行任务的入口:ThreadPoolExecutor.execute(Runnable command)(submit方法提交上来的callable实现类,最终被封装成了RunnableFuture的实现类,同时实现了Runable接口和Future接口,最终也是执行的execute方法)。先看线程池全局层面的执行逻辑。

public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException();
    // 如果工作线程数小于核心线程数,直接addworker(command,true)添加核心工作
    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);
        // 如果工作线程是0(核心线程数配置了0,第一个任务进来时,工作线程个数是0),添加一个非核心线程,任务从队列中获取
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false);
    }
    // 如果线程池处于非运行状态,或者BlockingQueue满了,添加非核心线程,添加非核心线程也失败时,执行拒绝策略
    else if (!addWorker(command, false))
        reject(command);
}

线程池的工作是由工作线程来处理的,工作线程的创建入口是addWorker,看addworker源码前先看一下线程池状态,有如下几种,用32位长度的高三位来表示线程池的状态,其中,running状态是 -1<<29。

// 各个状态值
private static final int RUNNING    = -1 << 29; -536870912- 01 + 00000000000000000000000000000private static final int SHUTDOWN   =  0 << 29; 		 000 + 00000000000000000000000000000private static final int STOP       =  1 << 29;  53687091201 + 00000000000000000000000000000private static final int TIDYING    =  2 << 29; 107374182410 + 00000000000000000000000000000private static final int TERMINATED =  3 << 29; 161061273611 + 00000000000000000000000000000

添加工作线程:ThreadPoolExecutor.addWorker(Runnable firstTask, boolean core)

// firstTask 工作线程创建后,要执行的首个任务(可能为空)
// core 创建的线程是否是核心线程
private boolean addWorker(Runnable firstTask, boolean core) {
    retry:
    // 自旋检查线程池状态
    for (;;) {
        int c = ctl.get();
        int rs = runStateOf(c);
        // 1,如果是运行状态,直接跳过(了解状态值后再看次判断逻辑)
        // 2,如果是shutdown状态,firstTask == null (添加的非核心线程),队列内容不为空时,可以尝试后面的添加流程,否则返回失败
        if (rs >= SHUTDOWN &&
            ! (rs == SHUTDOWN &&
               firstTask == null &&
               ! workQueue.isEmpty()))
            return false;
        // 第二次自旋,增加工作线程数量
        for (;;) {
            int wc = workerCountOf(c);
            // 参数core的唯一用处:工作线程数和控制线程数的比较,如果创建的是核心线程,和配置的核心线程数作比较,如果是非核心线程,和配置的最大线程数作比较
            // 如果工作线程打到了,允许的最大线程数,返回false,添加失败
            if (wc >= CAPACITY ||
                wc >= (core ? corePoolSize : maximumPoolSize))
                return false;
            // cas操作增加工作线程数,如果成功,跳出自旋,如果添加不成功,进入下一次自旋(最终要么在此处添加成功,要么在下一次自旋时添加失败)
            if (compareAndIncrementWorkerCount(c))
                break retry;
            c = ctl.get();
            // 再次检查线程池的状态
            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 {
                // 通过线程池锁,再次检查线程池状态和线程状态
                int rs = runStateOf(ctl.get());

                if (rs < SHUTDOWN ||
                    (rs == SHUTDOWN && firstTask == null)) {
                    if (t.isAlive()) // 如果工作线程内的线程提前启动了,抛出线程状态不正确的异常
                        throw new IllegalThreadStateException();
                    workers.add(w); // 将当前工作线程加入线程池
                    int s = workers.size();
                    if (s > largestPoolSize)
                        largestPoolSize = s;
                    workerAdded = true;
                }
            } finally {
                mainLock.unlock();
            }
            // 如果工作线程成功添加到了线程池内,启动工作线程的内部线程(newThread(this==worker)),实际调用worker的run方法。
            if (workerAdded) {
                t.start();
                workerStarted = true;
            }
        }
    } finally {
        if (! workerStarted)
            // 如果工作线程启动失败,恢复线程池ctl信息,中断工作线程,移除工作线程等操作
            addWorkerFailed(w);
    }
    return workerStarted;
}

创建工作线程的 Worker(Runnable firstTask) 构造方法中,需要注意,关于state 状态的使用

Worker(Runnable firstTask) {
    setState(-1); // 设置 aqs中节点状态为 -1 ,不允许内部线程被中断(因为工作线程刚初始化还未启动),此状态值的恢复在runWorker方法的unlock方法。
    this.firstTask = firstTask;
    this.thread = getThreadFactory().newThread(this);
}
// 线程池shutdownNow时会调用中断工作线程的方法,此处会中断worker内实际工作的线程。
private void interruptWorkers() {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        for (Worker w : workers)
            w.interruptIfStarted();
    } finally {
        mainLock.unlock();
    }
}   
void interruptIfStarted() {
    Thread t;
    // 如果当前worker刚初始化,state 为-1时,无需中断内部线程
    if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
        try {
            t.interrupt();
        } catch (SecurityException ignore) {
        }
    }
}

worker.t.start(),实际执行的是worker的run方法,因为worker本身实现了Runable接口。代码部分:this.thread = getThreadFactory().newThread(this)

worker 的run方法,又是调用的线程池的runWorker方法,下面是 runWorker(Worker w)的代码:

final void runWorker(Worker w) {
    Thread wt = Thread.currentThread();
    Runnable task = w.firstTask;
    w.firstTask = null;
    w.unlock(); // 此处通过worker的unlock方法恢复AQS中node的state值,目的是可以通过interrupt方式中断工作线程
    boolean completedAbruptly = true;
    try {
        // 如果firstTask != null 直接执行当前任务的run方法
        // 如果task 为空时,需要从阻塞队列中通过getTask获取一个待执行的任务,如果也返回null,结束while 循环,最终执行工作线程退出逻辑
        // runWork 执行完毕后,worker的run方法执行完毕,worker.t线程执行完毕,线程可以正常回收。
        while (task != null || (task = getTask()) != null) {
            w.lock();
            // 通过worker加锁后,再次根据线程池状态判断是否进行当前线程的中断
            if ((runStateAtLeast(ctl.get(), STOP) ||
                 (Thread.interrupted() &&
                  runStateAtLeast(ctl.get(), STOP))) &&
                !wt.isInterrupted())
                wt.interrupt();
            try {
                // 可扩展的线程池预留接口,任务执行前可以自定义一些处理,比如任务执行状态的更新等
                beforeExecute(wt, task);
                Throwable thrown = null;
                try {
                    // 真正要执行的run方法,递交到线程池里的Runable接口或者Callable接口
                    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();
            }
        }
        // 突然中止标记,为了减少线程池中工作线程的数量,正常执行到此处时,gettask方法内已经做了线程数量的累减,此处标记为false,finally方法内的工作线程退出时,就不需要再次累减工作线程的数量了
        completedAbruptly = false;
    } finally {
        processWorkerExit(w, completedAbruptly);
    }
}

通过getTask()方法,从队列中获取待执行的任务

private Runnable getTask() {
    boolean timedOut = false; // poll 方法获取任务是否超时
    for (;;) {
        int c = ctl.get();
        int rs = runStateOf(c);
        // 检查线程池状态和队列情况,如果线程池停止或者队列为空,需要结束工作线程,返回null之前,对线程池内工作线程的数量做累减(返回null后,工作线程的run方法就会执行完毕,执行完毕后正常执行线程的退出流程)
        if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
            decrementWorkerCount();
            return null;
        }
        int wc = workerCountOf(c);
        // 获取任务超时或者工作线程数大于核心线程数时,当前工作线程可以被淘汰(使用带超时时间的poll方法获取任务)
        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

        if ((wc > maximumPoolSize || (timed && timedOut))
            && (wc > 1 || workQueue.isEmpty())) {
            // 工作线程数量,大于最大线程数量;或者需要被淘汰时,cas方式减少工作线程数量,并返回null,结束工作线程的run方法
            if (compareAndDecrementWorkerCount(c))
                return null;
            continue;
        }

        try {
            // 核心工作线程,用阻塞式的take方法获取待执行任务,非核心工作线程,使用带超时时间的阻塞式poll方法获取待执行任务
            Runnable r = timed ?
                workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
            workQueue.take();
            // 如果顺利获取到了待执行任务,返回给worker,在worker的while循环中,执行r.run(),达到worker中线程复用的效果。
            if (r != null)
                return r;
            timedOut = true;
        } catch (InterruptedException retry) {
            timedOut = false;
        }
    }
}

最终回到线程池本身的最上层,工作线程已满且队列已满,或者线程池已经停止或者正在停止时,新来的任务会执行拒绝策略:

(RejectedExecutionHandler)handler.rejectedExecution(command(待执行), this(当前线程池))

拒绝策略比较简单就不再贴源码,线程池提供的拒绝策略有以下4种,默认的是AbortPolicy,源码:defaultHandler = new AbortPolicy();

AbortPolicy:直接抛异常:throw new RejectedExecutionException("Task " + r.toString() + " rejected from " + e.toString());

DiscardPolicy:空的实现方法,递交上来的任务进入拒绝策略后,什么也不做,直接丢弃。

DiscardOldestPolicy:从阻塞队列中通过poll()方法,移除一个等待最久的任务直接丢弃,再次尝试将当前任务递交线程池 e.execute®。

CallerRunsPolicy:直接在提交任务的主线程里直接执行 r.run()方法。

除这四种之外,还可以自己实现RejectedExecutionHandler接口,自己定制一个拒绝策略,比如等待几秒,重新添加到线程池,可以根据当前任务id设置重试次数;也可以根据当前的任务信息,恢复任务状态,或者其他处理等。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值