Java并发编程——Executor框架

内容

在公司中处理kafka任务时,我运用了线程池来创建多个线程处理来处理kafka命令。当然创建的线程是固定线程池,当时的考虑是因为资源的有限性,并且当时是对kafka中传过来的视频和图片多编解码,所以占用时间又点长。为了合理利用线程池,专门去学习关于这方面的知识,分享给大家!

线程池介绍

1.为什么要使用线程池?

  • 降低资源的消耗
  • 提高响应的速度
  • 提高线程的管理

2. 线程的工作原理

  • 线程池判断核心线程池的线程是否都在执行任务。如果不是,创建一个新的工作线程来执行任务。如果核心线程里面的线程都在执行任务,进入下一个流程
  • 线程池判断工作队列是否已满,如果未满,则将新的任务存储在这个工作队列里面,如果满了,进入下一个流程
  • 线程池判断线程池的线程是否都处于工作状态。如果没有,则创建一个新的工作线程来执行任务。如果已经满了,则交给饱和策略处理这个任务

3. ThreadPoolExecutor的核心参数

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) //后两个参数为可选参数
  • corePoolSize(核心线程数)
  • runnableTaskQueue(任务队列),常见的任务队列:ArrayBlockingQueue,LinkedBlockingQueue(FixThreadPool和SingleThreadPool使用这个队列,使maximumPoolSize参数失效),SynchronousQueue(CacheThreadPool使用这个队列存储任务,其实这种队列不存储元素),PriorityBlockingQueue(ScheduleThreadPool使用的DelayQueue其实就是这种队列)
  • maximumPoolSize(线程池最大数量)
  • ThreadFactory(用于设置线程的工厂)
  • RejectedExecutionHandler(饱和策略),主要有四个策略:AbortPolicy,直接抛出异常
    CallerRunsPolicy,只用调用者所在线程运行任务,DiscardOldestPolicy,丢弃队列中最近的一个任务,DiscardPolicy,不处理,丢弃掉

4.CacheThreadPool,SingleThreadExecutor,FixedThreadPool已知线程池之间的比较:

在这里插入图片描述
这三种线程池有它们固定的应用的场景
1.FixedThreadPool,适用于为了满足资源管理的需求,需要限制当前线程数量的应用场景,适用于负载比较重的服务器
2.SingleThreadExecutor,适用于保证顺序指定各个人物,并且在任意时间点,不会有多个线程是活动的应用场景
3.CachedThreadPool,是大小无界的线程池,适用于执行很多短期异步人物的小程序,或者是负载较轻的服务器

5.线程池创建多少线程是合理的

  • CPU密集型,创建线程数是cpu核数+1
  • I/O密集型,创建线程数是cpu核数*2

6.线程的监控

在我项目中,我通过在日志获取线程的一些指标数量来监控线程,主要通过以下的属性

  • taskCount
  • completeTaskCount
  • LargestPoolSize
  • getPoolSize
  • getActiveCount
  private void logThreaPoolInfo() {
        int activeCount = executorService.getActiveCount();
        long completedTaskCount = executorService.getCompletedTaskCount();
        int corePoolSize = executorService.getCorePoolSize();
        long keepAliveTimeSeconds = executorService.getKeepAliveTime(TimeUnit.SECONDS);
        int largestPoolSize = executorService.getLargestPoolSize();
        int maximumPoolSize = executorService.getMaximumPoolSize();
        int poolSize = executorService.getPoolSize();
        long taskCount = executorService.getTaskCount();
        logger.info("ActiveCount -> [" + activeCount + "] CompletedTaskCount -> ["
                + completedTaskCount + "] CorePoolSize -> [" + corePoolSize
                + "] KeepAliveTimeSeconds -> [" + keepAliveTimeSeconds
                + "] LargestPoolSize -> [" + largestPoolSize
                + "] MaximumPoolSize -> [" + maximumPoolSize
                + "] PoolSize -> [" + poolSize + "] TaskCount -> ["
                + taskCount + "]");
    }

——————————————————————————————————————

Executor框架

1.线程池和Executor框架有什么关系呢?

其实线程池的主要实现类是TheadPoolExecutor,它是Executor框架中最核心的类,Executor框架包括三部分:任务(Runable接口或Callable接口),任务的执行(执行的核心接口是Execotor),异步计算的结果(Future和实现Future接口的FutureTask类)
具体的使用示意图如下(该图片来自于《多线程编程艺术》书)
在这里插入图片描述

2.任务执行机制中Executor类图:

这里写图片描述

Executor:一个接口,其定义了一个接收Runnable对象的方法executor,其方法签名为executor(Runnable command)

ExecutorService:是一个比Executor使用更广泛的子类接口,其提供了生命周期管理的方法,以及可跟踪一个或多个异步任务执行状况返回Future的方法

AbstractExecutorService:ExecutorService执行方法的默认实现

ScheduledExecutorService:一个可定时调度任务的接口

ScheduledThreadPoolExecutor:ScheduledExecutorService的实现,一个可定时调度任务的线程池

ThreadPoolExecutor:线程池,可以通过调用Executors以下静态工厂方法来创建线程池并返回一个ExecutorService对象

Exector只是一个接口,规范,声明,并没有具体的实现功能,需要制定接口的实现类来完成制定的功能,但是threadPoolExecutor类虽然是Executor的实现类,但是实例化需要传入很多的参数,考虑线程并发与线程池等参数,所以官方建议是使用Executors工厂类来创建线程池对象。
这里写图片描述

3.FutureTask讲解

FutureTask实现了Future接口,也实现了Runnable接口。它的方法get会使当前线程阻塞,当线程处于完成状态的时候会返回结果。cancel方法在不同的状态会呈现结果不同,在线程没有启动的时候,任务不会执行,当线程启动但是中断了,返回为true,不中断返回false,结束状态返回false。
它的具体实现是基于AQS,abstractQueuedSynchronizer,后续我们在专门研究它。

总结

本文先讲解到这里,希望对你有帮助!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值