以上述方法构造出来的线程池,他在提交任务的时候是不会上来就直接创建一个线程的,会先做入队操作。如下图位置:
并且放入时,还会进行加锁
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,如果有立马执行。
这种类型的可能平时用的少,大家也可以不用太关注,主要不同的地方就是里面用到的队例。
只有1个线程,不停地处理提交到无界队列的任务同fixed类型。
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原理。