重走JAVA之路(五):面试又被问线程池原理?教你如何反击

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

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

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

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

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

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

正文

3.执行流程

execute()

public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
/*

  • Proceed in 3 steps:
    1. If fewer than corePoolSize threads are running, try to
  • start a new thread with the given command as its first
  • task. The call to addWorker atomically checks runState and
  • workerCount, and so prevents false alarms that would add
  • threads when it shouldn’t, by returning false.
    1. If a task can be successfully queued, then we still need
  • to double-check whether we should have added a thread
  • (because existing ones died since last checking) or that
  • the pool shut down since entry into this method. So we
  • recheck state and if necessary roll back the enqueuing if
  • stopped, or start a new thread if there are none.
    1. If we cannot queue task, then we try to add a new
  • thread. If it fails, we know we are shut down or saturated
  • and so reject the task.
    */
    int c = ctl.get();
    //如果当前线程数量小于核心线程数量,执行addWorker创建新线程执行command任务
    if (workerCountOf© < corePoolSize) {
    if (addWorker(command, true))
    return;
    c = ctl.get();
    }
    //如果当前是运行状态,将任务放入阻塞队列,double-check线程池状态
    if (isRunning© && workQueue.offer(command)) {
    int recheck = ctl.get();
    //如果再次check,发现线程池状态不是运行状态了,移除刚才添加进来的任务,并且拒绝改任务
    if (! isRunning(recheck) && remove(command))
    reject(command);
    //处于运行状态,但是没有线程,创建线程
    else if (workerCountOf(recheck) == 0)
    addWorker(null, false);
    }
    //往线程池中创建新的线程失败,则reject任务
    else if (!addWorker(command, false))
    reject(command);
    }

这里大概总结下execute方法的执行流程,其实大家看源码方法注释是一样很好的学习方法

  • 首先判断当前线程数量是不是比核心线程数量少,如果是,直接创建核心线程执行任务,否则走第二步
  • 如果当前线程数量等于核心线程数量了,那么就任务排期,将任务放进任务队列,放入成功后,再次check线程池状态,这里说明一下,在多线程的环境下,ctl.get()这个方法并不是一个原子操作,如果加入队列后,线程池状态改变了,不是RUNNING状态,那么这个任务将永远不会被执行,所以需要再次check,如果不是RUNNING状态,移除任务并拒绝任务,如果是RUNNING状态并且当前没有线程,则直接创建线程
  • 走到这一步前提就是第二步中的添加队列失败了,也就是任务队列满了,那么这个时候就考虑到创建非核心线程去执行任务,如果添加非核心线程也失败,那就直接拒绝

这里注意一点,当核心线程满的时候,并不会去直接创建非核心线程去执行任务,而是先放进任务队列,可以理解为需求任务首先是需要让内部核心员工去完成的,任务队列的优先级是高于非核心员工的,addWorker(),这里的传进去的boolean值,就代表着创建核心线程或者非核心线程

reject()

final void reject(Runnable command) {
handler.rejectedExecution(command, this);
}

拒绝任务很简单,reject方法会调用handler的rejectedExecution(command,this)方法,handler是RejectedExecutionHandler接口,默认实现是AbortPolicy,下面是AbortPolicy的实现:

public static class AbortPolicy implements RejectedExecutionHandler {
public AbortPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
}

可以看到默认策略是直接抛出异常的,这只是默认使用的策略,可以通过实现接口实现自己的逻辑。

addWorker()

private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;😉 {
int c = ctl.get();
int rs = runStateOf©;
// 这里return false的情况有以下几种
//1.当前状态是stop及以上 2.当前是SHUTDOWN状态,但是firstTask不为空
//3.当前是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;
if (compareAndIncrementWorkerCount©)
break retry;
c = ctl.get(); // Re-read ctl
if (runStateOf© != 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()) // 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;
}

这里就主要流程分析下

  • 2层循环,外部循环查询线程池状态,如果当前是stop及之上的状态,直接return,如果是SHUTDOWN状态,并且firstTask不为空或者队列中是空的,直接return
  • 内部循环查询线程数量,通过传递进来的boolean值,分别和核心线程以及最大线程数量进行对比,如果成立,worker数量+1,并且跳出循环。
  • 跳出循环就是实际执行任务了,Worker就将工作线程和任务封装到了自己内部,我们可以将Worker看成就是一个工作线程,至于Worker是如何执行任务和从阻塞队列中取任务

Worker()

private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable {
/** Thread this worker is running in. Null if factory fails. /
final Thread thread;
/
* Initial task to run. Possibly null. /
Runnable firstTask;
/
* Per-thread task counter */
volatile long completedTasks;
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
public void run() {
runWorker(this);
}

}

可以看到,Worker内部维护,一个线程变量以及任务变量,启动一个 Worker对象中包含的线程 thread, 就相当于要执行 runWorker()方法, 并将该 Worker对象作为该方法的参数.

runWorker()

final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
//task不为空,执行当前任务,任务执行完后将task置位空,getTask方法接着不断从队列中取任务
while (task != null || (task = getTask()) != null) {
w.lock();
//再次check线程池状态,如果是stop状态,直接interrupt()中断任务
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();

总目录展示

该笔记共八个节点(由浅入深),分为三大模块。

高性能。 秒杀涉及大量的并发读和并发写,因此支持高并发访问这点非常关键。该笔记将从设计数据的动静分离方案、热点的发现与隔离、请求的削峰与分层过滤、服务端的极致优化这4个方面重点介绍。

一致性。 秒杀中商品减库存的实现方式同样关键。可想而知,有限数量的商品在同一时刻被很多倍的请求同时来减库存,减库存又分为“拍下减库存”“付款减库存”以及预扣等几种,在大并发更新的过程中都要保证数据的准确性,其难度可想而知。因此,将用一个节点来专门讲解如何设计秒杀减库存方案。

高可用。 虽然介绍了很多极致的优化思路,但现实中总难免出现一些我们考虑不到的情况,所以要保证系统的高可用和正确性,还要设计一个PlanB来兜底,以便在最坏情况发生时仍然能够从容应对。笔记的最后,将带你思考可以从哪些环节来设计兜底方案。


篇幅有限,无法一个模块一个模块详细的展示(这些要点都收集在了这份《高并发秒杀顶级教程》里),麻烦各位转发一下(可以帮助更多的人看到哟!)

由于内容太多,这里只截取部分的内容。

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

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

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

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

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

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值