【Java并发编程系列8】多线程实战
主要基于小米最近的多线程项目,抽离出里面的多线程实例。
昨天听到袁老去世的消息,非常震惊,一代巨星的陨落,希望袁老在天堂能安好,贴了一张袁老的照片,这张照片也让我想起已在天堂的爷爷。
前言
Java多线程的学习,也有大半个月了,从开始学习Java多线程时,就给自己定了一个小目标,希望能写一个多线程的Demo,今天主要是兑现这个小目标。
这个多线程的示例,其实是结合最近小米的一个多线程异步任务的项目,我把里面涉及到多线程的代码抽离出来,然后进行一定的改造,之所以不自己重写一个,一方面是自己能力还不够,另一方面是想学习现在项目中多线程的实现姿势,至少这个示例是实际项目中应用的。先学习别人怎么造轮子,后面就知道自己怎么去造轮子了。
业务需求
做这个多线程异步任务,主要是因为我们有很多永动的异步任务,什么是永动呢?就是任务跑起来后,需要一直跑下去,比如消息Push任务,因为一直有消息过来,所以需要一直去消费DB中的未推送消息,就需要整一个Push的永动异步任务。
我们的需求其实不难,简单总结一下:
- 能同时执行多个永动的异步任务;
- 每个异步任务,支持开多个线程去消费这个任务的数据;
- 支持永动异步任务的优雅关闭,即关闭后,需要把所有的数据消费完毕后,再关闭。
完成上面的需求,需要注意几个点:
- 每个永动任务,可以开一个线程去执行;
- 每个子任务,因为需要支持并发,需要用线程池控制;
- 永动任务的关闭,需要通知子任务的并发线程,并支持永动任务和并发子任务的优雅关闭。
项目示例
线程池
对于子任务,需要支持并发,如果每个并发都开一个线程,用完就关闭,对资源消耗太大,所以引入线程池:
public class TaskProcessUtil {
// 每个任务,都有自己单独的线程池
private static Map executors = new ConcurrentHashMap<>();
// 初始化一个线程池
private static ExecutorService init(String poolName, int poolSize) {
return new ThreadPoolExecutor(poolSize, poolSize,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue(),
new ThreadFactoryBuilder().setNameFormat("Pool-" + poolName).setDaemon(false).build(),
new ThreadPoolExecutor.CallerRunsPolicy());
}
// 获取线程池
public static ExecutorService getOrInitExecutors(String poolName,int poolSize) {
ExecutorService executorService = executors.get(poolName);
if (null == executorService) {
synchronized (TaskProcessUtil.class) {
executorService = executors.get(poolName);
if (null == executorService) {
executorService = init(poolName, poolSize);
executors...
回复