JUC中线程池原理解析

更新时间:2023-06-30 10:11:16 阅读: 评论:0

JUC中线程池原理解析
JUC中线程池原理解析
⾸先我们先说⼀下⼀个线程池基本上会有哪些基本组件构成
1. 线程管理器,即线程池,维护线程池中线程数量,当线程空闲数多时尽⼼线程的回收,当线程紧张时进⾏线程的创建
2. 任务添加与拒绝策略
3. 任务队列 BlockingQueue
4. 线程的构造⼯⼚ ThreadFactory
5. 线程集合
paint是什么意思中文
6. worker 即线程中相当于runabble 的固定的模板⽅法
这⾥我写了⼀个简易版的线程池,便于让⼤家了解线程池原理,放到了csdn上
juc 中线程池源码分析
⾸先我们看juc 中ThreadPoolExecutor类的继承关系图
execute源码分析
分析
如果我们要使⽤线程肯定是要到线程中去提交任务的,所以我们就以execute ⽅法为⼊⼝点,进⾏剖
析。
public interface Executor {
//我们添加任务时调⽤的函数
void execute(Runnable command);
}
public void execute(Runnable command){
if(command ==null)
throw new NullPointerException();
few是什么意思int c = ();
//当前线程数⼩于⼯作线程数的话,添加⼀个worker
if(workerCountOf(c)< corePoolSize){
if(addWorker(command,true))
return;
c = ();
}
//当前线程⼤于核⼼线程数,将任务放⼊队列中,如果队列没有满则进⼊if⾥⾯,队列满调到el if,,需要特别注意的是offer⽅法并不会阻塞,但是放⼊失败也不会抛出异常,只会返回插⼊成功或失败
if(isRunning(c)&& workQueue.offer(command)){
int recheck = ()
//线程池被关闭时拒绝提交任务
if(!isRunning(recheck)&&remove(command))
reject(command);
el if(workerCountOf(recheck)==0)
addWorker(null,fal);
}
//当前线程⼤于核⼼线程数,并且放⼊队列失败,即队列满了,构造新的线程并执⾏该任务,但是线程数量⼤于maximumPoolSiz调⽤拒绝策略
el if(!addWorker(command,fal))
reject(command);
}
//addWorker 代码核⼼逻辑为创建⼀个worker,填充我们的任务并启动他的线程,然后加⼊⼯作者队列
private boolean addWorker(Runnable firstTask,boolean core){
//当前线程数量⼤于等于 maximumPoolSize 返回fal
if(wc >= CAPACITY ||
wc >=(core ? corePoolSize : maximumPoolSize))
return fal;
w =new Worker(firstTask);
final Thread t = w.thread;
workerStarted = workers.add(w);
教育培训哪家好t.start();
return workerStarted ;
}
总结
关于何时分配线程,何时添加队列策略
通过execute() ⽅法的剖析,其⼤致逻辑为当在execute(Runnable)⽅法中提交⼀个新任务,并且运⾏的线程少于
corePoolSize线程时,就会创建⼀个新线程来处理该请求,即使其他⼯作线程处于空闲状态。 如果运⾏的线程⼤于
corePoolSize但⼩于maximumPoolSize,则只有在队列满时才会创建⼀个新线程。下⾯我们对worker 进⾏分析。
关于何时调⽤拒绝策略
在⽅法execute(Runnable)中提交的新任务将在Executor关闭时被拒绝,当Executor对最⼤线程和⼯作队列容量都使⽤有限的界限,并且达到饱和时也会被拒绝。需要特别注意的是offer⽅法并不会阻塞,但是放⼊失败也不会抛出异常,只会返回插⼊成功或失败。
worker 源码分析
公主殿下love生活有中文翻译分析
private final class Worker
tumor biology
extends AbstractQueuedSynchronizer
implements Runnable
{
//线程
final Thread thread;
//我们提交的任务,可能为空
Runnable firstTask;
Worker(Runnable firstTask){
this.firstTask = firstTask;
//通过线程⼯⼚获取线程,,,,注意这⾥把this 放⼊了,也就是我们的run ⽅法是在另外⼀个线程中调⽤的屁
this.thread =getThreadFactory().newThread(this);
belarus}
//线程池中线程具体执⾏逻辑,具体的逻辑在runWorker中
public void run(){
runWorker(this);
}
//暂且忽略锁的逻辑
}
final void runWorker(Worker w){
Runnable task = w.firstTask;
w.firstTask =null;
try{
/
/通过getTask获取任务,值得注意的是getTask  ==  null 也是线程的退出条件哦
while(task !=null||(task =getTask())!=null){
try{
//在任务执⾏前的回调函数
beforeExecute(wt, task);
Throwable thrown =null;
try{
task.run();
}catch(RuntimeException x){
thrown = x;throw x;
}catch(Error x){
2017研究生分数线
thrown = x;throw x;
}catch(Throwable x){
thrown = x;throw new Error(x);
}finally{
//在任务执⾏后的回调函数
afterExecute(task, thrown);
}
}finally{
}
}
completedAbruptly =fal;
}finally{
//线程因为异常退出的回调函数
processWorkerExit(w, completedAbruptly);
}
}
总结
worker 的执⾏逻辑很简单,只是while 的去任务队列获取数据,执⾏其run⽅法,但是需要特别指指出的是 在任务执⾏前的回调函数
在任务执⾏后的回调函数 线程因为异常退出的回调函数 ,我们可以利⽤这⼏个钩⼦⽅法做任务执⾏前后的特殊处理哦。还有 值得注意的是getTask == null 也是线程的退出条件哦。
getTask() ⽅法源码分析
分析
⼤家可能会问了,⼀个简单的getTask ⽅法有什么好说的,不就是从队列中获取数据吗,但是不⽌这么简单,结合上⾯线程的退出条件,可以做到动态的线程池中线程的回收⼯作。
private Runnable getTask(){
boolean timedOut =fal;// Did the last poll() time out?
for(;;){
int c = ();
int rs =runStateOf(c);
// Check if queue empty only if necessary.
if(rs >= SHUTDOWN &&(rs >= STOP || workQueue.isEmpty())){
decrementWorkerCount();
return null;
}
int wc =workerCountOf(c);
//是否启⽤超时
dustinboolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
//条件1  当前线程数⼤于核⼼线程数并且获取数据等待keepAliveTime时间后超时
if((wc > maximumPoolSize ||(timed && timedOut))
&&(wc >1|| workQueue.isEmpty())){
if(compareAndDecrementWorkerCount(c))
return null;
continue;
}
try{
/
/如果启⽤超时的话,就设置超时时间,否则直接阻塞等待
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS):
workQueue.take();
if(r !=null)
return r;
timedOut =true;
}catch(InterruptedException retry){
timedOut =fal;
}
}六级作文万能句式
}
总结
简单说这段代码的意思就是 如果当前池中有超过corePoolSize的线程,如果空闲的线程超过了keepAliveTime的时间间隔,那么多余的线程将通过 while (gettask != null)条件成⽴⽽退出被终⽌。否则调⽤的是BlockQueue 对象的take ⽅法
submit源码分析
分析
⼤家都知道,线程池的submit ⽅法,可以返回⼀个Futuer 的结果,并且传递的是Callable 接⼝的⽅法,这样我们就可以获取线程的执⾏结果了,但是原理是什么样的呢?我们⼀步步分析
public<T> Future<T>submit(Callable<T> task){
if(task ==null)throw new NullPointerException();
//将我们的Callable  包装为RunnableFuture  然后提交给线程池处理,估计结果的返回就是在这个类中做的⽂章,实际返回的是FutureTask实现类
RunnableFuture<T> ftask =newTaskFor(task);
execute(ftask);
return ftask;
}
protected<T> RunnableFuture<T>newTaskFor(Callable<T> callable){
return new FutureTask<T>(callable);
}
我们继续分析FutueTask实现类,其继承结构如下
>ballbusting中国

本文发布于:2023-06-30 10:11:16,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/fan/78/1070011.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:线程   任务   队列   中线   退出   分析
相关文章
留言与评论(共有 0 条评论)
   
验证码:
推荐文章
排行榜
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图