Java并发:线程池详解(ThreadPoolExecutor)(2),java框架面试题整理

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024b (备注Java)
img

正文

// 线程数容量,低29位表示有效的线程数, 0001 1111 1111 1111 1111 1111 1111 1111

private static final int CAPACITY = (1 << COUNT_BITS) - 1;

/**

  • 大小关系:RUNNING < SHUTDOWN < STOP < TIDYING < TERMINATED,

  • 源码中频繁使用大小关系来作为条件判断。

  • 1110 0000 0000 0000 0000 0000 0000 0000 运行

  • 0000 0000 0000 0000 0000 0000 0000 0000 关闭

  • 0010 0000 0000 0000 0000 0000 0000 0000 停止

  • 0100 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 workQueue;

// 锁

private final ReentrantLock mainLock = new ReentrantLock();

// 包含线程池中的所有工作线程,只有在mainLock的情况下才能访问,Worker集合

private final HashSet workers = new HashSet();

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包含一个线程、一个初始任务、一个任务计算器

*/

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(); // 拿到当前的ctl值

if (workerCountOf© < corePoolSize) { // 如果有效的线程数小于核心线程数

if (addWorker(command, true)) // 则新建一个线程来处理任务(核心线程)

return;

c = ctl.get(); // 拿到当前的ctl值

}

// 走到这里说明有效的线程数已经 >= 核心线程数

if (isRunning© && workQueue.offer(command)) {// 如果当前状态是运行, 尝试将任务放入工作队列

int recheck = ctl.get(); // 再次拿到当前的ctl值

// 如果再次检查状态不是运行, 则将刚才添加到工作队列的任务移除

if (! isRunning(recheck) && remove(command))

reject(command); // 并调用拒绝策略

else if (workerCountOf(recheck) == 0) // 如果再次检查时,有效的线程数为0,

addWorker(null, false); // 则新建一个线程(非核心线程)

}

// 走到这里说明工作队列已满

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©; // 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©; // 拿到有效的线程数

// 校验有效的线程数是否超过阈值

if (wc >= CAPACITY ||

wc >= (core ? corePoolSize : maximumPoolSize))

return false;

// 使用CAS将workerCount+1, 修改成功则跳出循环,否则进入下面的状态判断

if (compareAndIncrementWorkerCount©)

break retry;

c = ctl.get(); // 重新读取ctl

// 判断当前运行状态,如果不等于上面获取的运行状态rs,

// 说明rs被其他线程修改了,跳到retry重新校验线程池状态

if (runStateOf© != 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;

}

该方法主要目的就是使用入参中的firstTask和当前线程添加一个Worker,前面的for循环主要是对当前线程池的运行状态和有效的线程数进行一些校验,校验逻辑比较绕,可以参考注释进行理解。该方法涉及到的其他方法有addWorkerFailed(见下文addWorkerFailed源码解读);还有就是Worker的线程启动时,会调用Worker里的run方法,执行runWorker(this)方法(见下文runWorker源码解读)。

addWorkerFailed方法

/**

  • Rolls back the worker thread creation.

    • removes worker from workers, if present
    • decrements worker count
    • rechecks for termination, in case the existence of this
  • worker was holding up termination

*/

private void addWorkerFailed(Worker w) { // 回滚Worker的添加,就是将Worker移除

final ReentrantLock mainLock = this.mainLock;

mainLock.lock();

try {

if (w != null)

workers.remove(w); // 移除Worker

decrementWorkerCount(); // 有效线程数-1

tryTerminate(); // 有worker线程移除,可能是最后一个线程退出需要尝试终止线程池

} finally {

mainLock.unlock();

}

}

该方法很简单,就是移除入参中的Worker并将workerCount-1,最后调用tryTerminate尝试终止线程池,tryTerminate见下文对应方法源码解读。

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©; // 当前运行状态

// 如果线程池运行状态为停止,或者可以停止(状态为SHUTDOWN并且队列为空)

// 则返回null,代表当前Worker需要移除

if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {

decrementWorkerCount(); // 将workerCount - 1

// 返回null前将workerCount - 1,

// 因此processWorkerExit中completedAbruptly=false时无需再减

return null;

}

int wc = workerCountOf©; // 当前的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©)

// 返回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。

processWorkerExit方法

总结

互联网大厂比较喜欢的人才特点:对技术有热情,强硬的技术基础实力;主动,善于团队协作,善于总结思考。无论是哪家公司,都很重视高并发高可用技术,重视基础,所以千万别小看任何知识。面试是一个双向选择的过程,不要抱着畏惧的心态去面试,不利于自己的发挥。同时看中的应该不止薪资,还要看你是不是真的喜欢这家公司,是不是能真的得到锻炼。其实我写了这么多,只是我自己的总结,并不一定适用于所有人,相信经过一些面试,大家都会有这些感触。

**另外本人还整理收藏了2021年多家公司面试知识点以及各种技术点整理 **

下面有部分截图希望能对大家有所帮助。

在这里插入图片描述

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

一直在尝试获取工作队列的任务;否则,线程会有超时时间(keepAliveTime),当在keepAliveTime时间内获取不到任务,该线程的Worker会被移除。

Worker移除的过程:getTask方法返回null,导致runWorker方法中跳出while循环,调用processWorkerExit方法将Worker移除。注意:在返回null的之前,已经将workerCount-1,因此在processWorkerExit中,completedAbruptly=false的情况(即正常超时退出)不需要再将workerCount-1。

processWorkerExit方法

总结

互联网大厂比较喜欢的人才特点:对技术有热情,强硬的技术基础实力;主动,善于团队协作,善于总结思考。无论是哪家公司,都很重视高并发高可用技术,重视基础,所以千万别小看任何知识。面试是一个双向选择的过程,不要抱着畏惧的心态去面试,不利于自己的发挥。同时看中的应该不止薪资,还要看你是不是真的喜欢这家公司,是不是能真的得到锻炼。其实我写了这么多,只是我自己的总结,并不一定适用于所有人,相信经过一些面试,大家都会有这些感触。

**另外本人还整理收藏了2021年多家公司面试知识点以及各种技术点整理 **

下面有部分截图希望能对大家有所帮助。

[外链图片转存中…(img-gnY7PPBu-1713624361515)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
[外链图片转存中…(img-quMe2oyZ-1713624361515)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值