不断的学习,我们才能不断的前进
一个好的程序员是那种过单行线马路都要往两边看的人

线程池

FutureTask的作用?

FutureTask对象可以获取到线程执行的结果。FutureTask 继承于future接口和Runnable接口,future的get方法可以获得 线程执行的结果,而使用线程池的时候,只能传入Runnable接口的对象,而Runnable接口不能获得线程的返回值,所以需要使用callable接口,封装一个FutureTask对象,然后返回给外部线程一个Future句柄的get 方法来获取结果,而get 方法会阻塞获取结果的线程,直到任务完成。

FutureTask的初始化可以传入runnable接口 和 callable接口,如果传入runnable接口的话使用一个RunnableAdapter适配器变成callable接口(执行call方法,实际上执行的是runnable的run方法),返回值是null,在业务层面的FutureTask.get就会拿到一个null 值。

FutureTask的get操作会阻塞获取任务结果的线程,那么怎么唤醒这些线程呢?

FutureTask的任务状态有NEW(表示新建,还没开始处理)、COMPLETING(马上要完成,临界状态)、NORMAL(任务完成,有结果)、EXCEPTIONAL(出现异常)、CANCELLED(任务取消)、INTERRUPTING(执行中断中)、INTERRUPTED(已中断)。
FutureTask的get操作会先判断 任务是否是已经完成状态,如果是直接返回结果就ok;
如果是NEW 或者COMPLETING状态表示任务还没结束,然后阻塞调用get的线程,就是为当前线程创建WaitNode结点,然后头插法进入阻塞队列,最后执行 LockSupport.park方法进行阻塞,等待唤醒。

当FutureTask的run方法执行完成FutureTask里面业务逻辑的Runnable或者Callable方法之后,在没有异常的情况下,线程会更新FutureTask状态变成NORMAL,表示任务已经执行完成,然后会遍历整个等待队列,唤醒WaitNode结点内的线程,LockSupport.unpark方法唤醒等待线程。

被唤醒的线程继续执行FutureTask.get方法逻辑,检查FutureTask的状态,发现是NORMAL了,就直接获取到结果。

submit 和executor的区别?

最终调用的都是executor方法,只是submit把任务包装了一下,把传入Runnable 或者Callable接口的对象,封装成FutureTask对象,submit提交后会返回给用户一个句柄,这个句柄其实就是FutureTask对象,通过句柄的get方法可以获取到任务执行的结果

线程池的基本参数?

7大核心参数:核心线程池大小、最大线程池大小、空闲线程等待工作的超时时间、超时时间的单元、线程工厂、阻塞队列、拒绝策略
4大拒绝策略:拒绝任务并抛出异常(AbortPolicy,默认)、拒绝任务不抛出异常(DiscardPolicy)、和最早的任务竞争(DiscardOldestPolicy)、由当前线程执行任务(CallerRunsPolicy)。
Worker表示工作中的任务,继承于AQS模版。
线程池的状态:RUNNING表示可以正常接受任务并处理阻塞队列中任务、SHUTDOWN不能接受新的任务但是可以处理阻塞队列里的任务、STOP不能接受任务也不能处理阻塞队列里的任务、TIDYING所有任务都终止、TERMINATED什么都不做。

核心线程池大小和最大线程大小的具体设计?

如果提交任务的时候,工作线程少于corePoolSize线程,则使用addWorker 创建工作线程并执行任务,会创建一个Worker,Worker继承与AQS模版,其实就是包装了一个Thread线程,Worker里面有一个firsTask字段,就是Worker线程启动后会优先执行firsTask任务,然后再去执行阻塞队列里面的任务。提交的这个task会设置到Worker里面的firstTask里面。
如果工作线程大于核心线程 且 阻塞队列 未满,则把新的任务放入阻塞队列。
如果工作线程大于核心线程,小于最大线程数据,并且阻塞队列已满,则创建线程执行任务
如果工作线程大于最大线程数量,而且阻塞队列已满,则执行拒绝策略

线程池的五个状态

线程池的状态:RUNNING表示可以正常接受任务并处理阻塞队列中任务、SHUTDOWN不能接受新的任务但是可以处理阻塞队列里的任务、STOP不能接受任务也不能处理阻塞队列里的任务、TIDYING所有任务都终止、TERMINATED什么都不做。
RUNNING表示正常状态,线程池工作的正常状态
SHUTDOWN表示线程池被调用shutdown()函数,会遍历整个线程池中的线程,给空闲线程一个中断信号,被中断唤醒的线程因为没有获取到任务,接下来会执行线程退出的逻辑,如果阻塞队列里面还有任务没有完成会保留一个线程去执行这些任务。
stop状态会调用shutdownNow()方法,把线程池的状态改为STOP状态,会遍历所有的Worker线程,然后给线程一个中断信号,然后所有线程都不去消费任务队列,执行线程退出逻辑。
TIDYING
TERMINATED:就是线程池最后一个线程退出后会把线程池的状态修改为TERMINATED状态。

Worker为什么要用AQS独占锁?

用独占锁表明占用锁的线程是Worker内部线程本身,表明内部正在执行task.run方法,也就是执行业务之前会先获取锁,再执行业务完之后才会去释放锁。

Worker启动流程?

Worker继承了AQS 和 Runnable接口,在传入firstTask的构造方法中,会把自己注册进线程工厂中,在Thread启动的时候,会以Worker.run方法作为入口。
在提交任务的时候会创建Worker,然后加入到WorkerSet工作线程池里面,然后由提交的线程获取woker的Thread,使用thread.start方法启动worker线程。

WorkerSet是Hashset是线程不安全的还要加ReentrantLock锁

Worker启动后做什么?

Worker启动后会调用run方法,run方法调用runWorker在runWorker里面有个while循环执行task,会先获取worker里面的firstask来执行,如果firstask为null,然后使用getTask()方法去阻塞队列里面获取任务执行,getTask会阻塞直到获取到任务或者超时,获取到线程后会先执行interrupted方法获取到中断标志位,然后重置中断标志位为false,然后在执行任务。
没有获取到任务就退出。


目录