Java线程池的使用

来源:互联网 发布:linux tmp 清理 编辑:程序博客网 时间:2024/05/16 00:51

  最近接到一个需求,优化某个请求的性能。具体情况是这样的,之前的实现是前台每隔一段时间向后台请求数据,后台根据请求去实时获取需要的数据,这需要服务器通过内部网络轮询上百台设备,将他们的信息都get到,组装好,返回给前台,整个流程不用了解得很具体,只需要知道这比操作数据库慢很多很多!!最初实现时考虑的是数据的时效性,要反应最新的设备信息,但后来维护的设备数量上百的级别后,性能就是大问题了,经过组内讨论以及CCB评审后,决定改,不再强求数据时效性,保证性能再想其他。
  最后方案是后台起个定时任务去获取设备数据,将拿到的数据放到缓存,前台请求直接取缓存数据,大致结构就像这样:
  缓存实现
  原流程没有缓存和另一个线程,所以一个http就触发一次扫描所有设备。现在改为后台定时获取并更新缓存,前台只拿缓存数据,不用等服务器花很长时间获取数据后才能拿到想要的结果。UI界面的定时器间隔(用户可自定义)不会影响服务器的性能,修改点就是需要新增一个变量作为缓存,并另起一个定时线程。为了适配具体业务,以及更精细的控制线程的启动与停止,增加了时间戳等属性,这里不详细叙述,仅记录线程相关的处理。
  项目中线程都是以线程池的形式起的,资源池化的好处不用多说。java 8中线程相关的包为java.util.concurrent。定时任务的线程池的接口为ScheduledExecutorService ,实例化方式为Executors.newSingleThreadScheduledExecutor() 。下面重点是ScheduledExecutorService 接口的使用。

  1. ScheduledExecutorService 提交新任务常用的两个方法:scheduleAtFixedRate与scheduleWithFixedDelay,他们都是定时执行扔给线程池的任务,但两者有区别:

    scheduleAtFixedRate 指定的任务间隔是,相邻两次任务开始时刻的间隔
    scheduleWithFixedDelay 指定的任务间隔是,前一次任务结束的时刻与下一次任务开始时刻的间隔

    假设两个方法都指定间隔为1min,具体图示如下:

线程池参数说明
 
 2. 关闭与重启线程池,ScheduledExecutorService 继承自父类的关闭资源池的方法shutdown
 注意该方法试图将整个资源池关闭,并终止掉池中的所有任务。最初曾疑惑过,为什么api中只有shutdown,没有启动呢?仔细想想就清楚了,shutdown的工作是关闭资源池,释放资源,如果需要再启动,请重新实例化线程池,重新申请资源!!!这里的shutdown是将底层资源都释放了,而没有保留资源待再次启动,所以没有启动方法来误导调用者。
 3. 定时任务的终止与重新提交
 定时任务没有指定任务结束的时机,不人为结束的话,将一直执行。
 在api文档中ScheduledExecutorService开头有个例子,可以看到将任务提交给线程池后都会返回一个ScheduledFuture ,这个对象就是对此处提交任务的引用,可以使用future.cancel(true);终止定时任务的执行。
 任务重新提交与创建新任务一样。
 需要注意,当线程池被shutdown后,在提交任务将会报错。目前已知,结束已有任务的唯一途径就是通过future对象实现。
 4. 遗留问题:
 创建一个容量为1的线程池,提交多个定时任务,这些任务是交替执行?还是只有第一个提交的任务执行,其他任务都在等待中,只有等正在处理的任务被取消,才会执行下一个任务?
 理论上来说线程池只是管理线程,而定时任务只是特殊一点的线程—通过定时休眠,循环执行任务来实现,由此推测应该是只有一个任务执行而不是交替执行。但也不排除其内部特意优化过的可能。
 本次使用因为即支持获取所有设备数据,也支持获取指定设备数据,所以使用到了线程的取消和重新提交,但因为缓存使用的是同样的变量,也因为代码洁癖而一定要清理没用的任务,每次提交新任务之前都是将之前的任务干掉的,没有试验遗留问题,待验证。

//2016.7.27更新
  问题回归时发现异常:即支持获取所有,也支持获取单台,实现时是根据给线程的入参判断的。如果多个中断需要获取不同的设备信息,会导致数据被频繁的清空,重新获取,而且得到的数据为错误数据。
  得到的教训,定时线程任务维护缓存,只做一件单一的事,过滤任务不能放在任务中,应该是任务获取所有信息,从缓存中取用数据时做过滤。
  缓存数据也不能直接清空。因为同时存取会有同步问题。处理方法是:遍历一遍旧数据,删除没有的元素;在遍历一遍新数据,更新已有的元素和增加新元素。(存在新数据删除某项和新数据新增某项,所有需要遍历两遍)

0 0
原创粉丝点击