作者:若丨寒 https://www.jianshu.com/p/bc978c220da6
线程池源码也是面试经常被提问到的点,我会将全局源码做一分析,然后告诉你面试考啥,怎么答。
简洁的答两点就行。
1.创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
2.(JDK8新增)会根据所需的并发数来动态创建和关闭线程。能够合理的使用CPU进行对任务进行并发操作,所以适合使用在很耗时的任务。
注意返回的是ForkJoinPool对象。
什么是ForkJoinPool:
使用一个无限队列来保存需要执行的任务,可以传入线程的数量;不传入,则默认使用当前计算机中可用的cpu数量;使用分治法来解决问题,使用fork()和join()来进行调用。
3.创建一个可缓存的线程池,可灵活回收空闲线程,若无可回收,则新建线程。
4.创建一个单线程的线程池。
5.创建一个定长线程池,支持定时及周期性任务执行。
Executor结构:
一个运行新任务的简单接口
扩展了Executor接口。添加了一些用来管理执行器生命周期和任务生命周期的方法
对ExecutorService接口的抽象类实现。不是我们分析的重点。
Java线程池的核心实现。
值的注意的是状态值越大线程越不活跃。
在向线程池提交任务时,会通过两个方法:execute和submit。
本文着重讲解execute方法。submit方法放在下次和Future、Callable一起分析。
总结一下它的工作流程:
通过上面的execute方法可以看到,最主要的逻辑还是在addWorker方法中实现的,那我们就看下这个方法:
主要工作是在线程池中创建一个新的线程并执行
参数定义:
因为workers是HashSet类型的,不能保证线程安全。
那w = new Worker(firstTask);如何理解呢
可以看到它继承了AQS并发框架还实现了Runnable。证明它还是一个线程任务类。那我们调用t.start()事实上就是调用了该类重写的run方法。
tryAcquire方法它是不允许重入的,而ReentrantLock是允许重入的。对于线程来说,如果线程正在执行是不允许其它锁重入进来的。
线程只需要两个状态,一个是独占锁,表明正在执行任务;一个是不加锁,表明是空闲状态。
run方法又调用了runWorker方法:
总结一下runWorker方法的执行过程:
1、while循环中,不断地通过getTask()方法从workerQueue中获取任务
2、如果线程池正在停止,则中断线程。否则调用3.
3、调用task.run()执行任务;
4、如果task为null则跳出循环,执行processWorkerExit()方法,销毁线程workers.remove(w);
这个流程图非常经典:
除此之外,ThreadPoolExector还提供了tryAcquire、tryRelease、shutdown、shutdownNow、tryTerminate、等涉及的一系列线程状态更改的方法有兴趣可以自己研究。大体思路是一样的,这里不做介绍。
tryAcquire方法它是不允许重入的,而ReentrantLock是允许重入的。对于线程来说,如果线程正在执行是不允许其它锁重入进来的。
线程只需要两个状态,一个是独占锁,表明正在执行任务;一个是不加锁,表明是空闲状态。
shutdown方法与getTask方法存在竞态条件.(这里不做深入,建议自己深入研究,对它比较熟悉的面试官一般会问)
End
本文分享自 MoziInnovations 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!