一、CachedThreadPool类型

以上述方法构造出来的线程池,他在提交任务的时候是不会上来就直接创建一个线程的,会先做入队操作。如下图位置:

并且放入时,还会进行加锁

cached线程池基本上可以确认说没有线程在等待获取任务的时候,入队直接是返回false的,不让你入队成功,必须要有人在等待获取任务,才能入队成功,然后尝试添加一个非核心线程去执行任务。

他其实是用来干两个线程之间的数据传递的同步,offer+poll,poll的时候,如果没人offer,此时你会阻塞,直到有人offer,最后一次poll的人先可以获取到别人offer的数据,栈的效果。

SynchronousQueue的原理,TransferStack来进行数据传递。

1、offer+poll的方式,才能实现他原本希望实现的一个效果,如果poll的时候没有人在offer,此时就会将head指针指向poll操作,poll线程就park挂起。

2、再次另外一个线程来offer,head指针指向最新的线程poll的数据,head不为空,就把offer放入队列,然后换醒poll线程获取任务执行。

3、offer的时候,如果没人poll,他是不会阻塞的,多人offer,最后一个offer是在栈顶,此时有人poll,先获取最后一次offer的数据,栈后进先出,queue先进先出,默认是栈的实现

4、SynchronousQueue的offer,他是不会阻塞的,如果没有poll,offer的时候直接就是返回null,就是入队失败。

5、此时如果有已经有一个非core线程在执行任务,再来第二个任务要提交,此时会如何呢?当前线程数量 = 1,corePoolSize = 0,1 < 0?不成立,所以此时还是不会直接创建一个core线程出来。

6、非core线程在从队列获取任务的时候走的是poll,非阻塞的,而且有超时时间,keepAliveTime,如果超过指定时间没获取到任务,此时当前线程就会自动释放掉,进入SynchronousQueue的栈。

他会位于栈顶,但是挂起当前线程的时间超时时间是60s,超过60s的话,就认为此次poll就失败了,此时就会返回一个null,自动触发这个非core线程的释放。

7、如果没有线程在从队列获取任务,此时无论你提交多少任务,SynchronousQueue入队都是失败的,offer都是返回false,此时都会直接创建线程来执行任务的,但是如果有线程空闲出来就会尝试从队列poll任务。

8、如果队列为空,此时最多会等待60s空闲去poll一个任务出来,如果超过了60s没有poll到任务,此时这个线程自己就会释放掉。

9、Cached类型基本是不可能触发reject(拒绝)的,因为在需要的时候无限制的创建新的线程来处理新的任务,提交的任务几乎是不会排队的,永远能最快速度的得到执行,入队的时候先看看有没有人空闲在poll,如果有立马执行。

这种类型的可能平时用少,大家也可以不用太关注,主要不同的地方就是里面用到的队例。

二、SingleThreadExecutor类型

只有1个线程,不停地处理提交到无界队列的任务同fixed类型。

三、ScheduledThreadPool类型

DelayedWorkQueue延迟队列,扔进去的任务会创建一个指定的时间的延迟任务decorateTask,比如说5秒,然后放入到延迟队列中worker中,到了指定时间,会启动线程,执行runWorker然后调用到ScheduledThreadPoolExecutor去执行队列任务,调用run方法,去执行目标任务。

也就是说,你去创建一个延迟任务放入队列中,队列中有调度任务,到了指定的时间就会从队列中取出来去执行。这个就会用到拒绝策略。

四、面试相关问题

1、当队列满了,核线程空闲、扩展线程也空闲,谁从队列获取?

答:都会去获取。

2、为什么线程池要先创建coreSize的核线程?

答:使用核心线程,避免线程频繁创建与释放带来的耗时。

3、如果额外线程都创建完了,队列还是满的,此时还有新的任务来怎么办?

答:只能reject走reject策略,可以传入RejectedExecutionHandler。

有以下几种reject策略:

(1)AbortPolicy:默认拒绝策略,抛出异常,及时反馈程序运行状态。

(2)DiscardPolicy:丢弃后续提交的任务,但是不抛出异常。

(3)DiscardOldestPolicy:丢弃队列最前面的任务,然后重新提交被拒绝的任务。

(4)CallerRunsPolicy:由调用线程处理该任务.

(5)自定义一个reject策略:如写入磁盘或消息,有机会重新拿取执行。

4、线程池中submit()execute()方法有什么区别?

答:都可以提交任务,execute()返回类型是void, 而submit()/invoke()方法可以返回持有计算结果的Future对象。可以向线程池提交的任务有两种:Runnable(无返回值)和Callable(有返回值)

前面讲的安全队例及线程池,底部都是基一些AQS的安全机制,下节我们来讲解一下:AQS原理。

举报/反馈

创业者N号

271获赞 70粉丝
好记忆不如小分享,记录点点滴滴。
关注
0
0
收藏
分享