Java实现终止线程池中正在运行的定时任务

最近项目中遇到了一个新的需求,就是实现一个可以动态添加定时任务的功能。说到这里,有人可能会说简单啊,使用quartz就好了,简单粗暴。然而quartz框架太重了,小项目根本不好操作啊。当然,也有人会说,jdk提供了timer的接口啊,完全够用啊。但是我们项目的需求完全是多线程的模型啊,而timer是单线程的,so,楼主最后还是选择了jdk的线程池。

线程池是什么

Java通过Executors提供四种线程池,分别为:
newCachedThreadPool :创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
newFixedThreadPool : 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
newScheduledThreadPool : 创建一个定长线程池,支持定时及周期性任务执行。
newSingleThreadExecutor : 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

楼主项目中用到的是newScheduledThreadPool, 就这些吧,再多的楼主就班门弄斧了,Google一下,一大堆。

线程池service的获取

楼主通过单例模式来获取线程池的service,代码如下:

/**
 * 线程池创建.
 * @author wuhf
 * @date 2018/01/16
 */
public class ThreadPoolUtils {

  private static ScheduledExecutorService executorService;

  private ThreadPoolUtils() {
    //手动创建线程池.
    executorService = new ScheduledThreadPoolExecutor(10,
        new BasicThreadFactory.Builder().namingPattern("syncdata-schedule-pool-%d").daemon(true).build());
  }

  private static class PluginConfigHolder {
    private final static ThreadPoolUtils INSTANCE = new ThreadPoolUtils();
  }

  public static ThreadPoolUtils getInstance() {
    return PluginConfigHolder.INSTANCE;
  }

  public ScheduledExecutorService getThreadPool(){
    return executorService;
  }

}

中断某一个正在运行的线程代码实现

废话就不多说了,代码如下:

/**
 * 中断线程池的某个任务.
 */
public class InterruptThread implements Runnable {

  private int num;

  public InterruptThread (int num){
    this.num = num;
  }

  public static void main(String[] args) throws InterruptedException {

    Thread interruptThread = new Thread(new InterruptThread(1));
    ScheduledFuture<?> t = ThreadPoolUtils.getInstance().getThreadPool().scheduleAtFixedRate(interruptThread,0,2,
        TimeUnit.SECONDS);

    InterruptThread interruptThread1 = new InterruptThread(2);
    ThreadPoolUtils.getInstance().getThreadPool().scheduleAtFixedRate(interruptThread1,0,2,
        TimeUnit.SECONDS);

    InterruptThread interruptThread2 = new InterruptThread(3);
    ThreadPoolUtils.getInstance().getThreadPool().scheduleAtFixedRate(interruptThread2,0,2,
        TimeUnit.SECONDS);
    Thread.sleep(5000);

		//终止正在运行的线程interruptThread
    t.cancel(true);
    while (true){

    }
  }

  @Override
  public void run() {
    System.out.println("this is a thread" + num);
  }
}

踩坑记录

楼主在使用如下代码时,突然想到当这个定时任务需要被停止时该如何停止线程运行

ThreadPoolUtils.getInstance().getThreadPool().scheduleAtFixedRate(interruptThread,0,2,
        TimeUnit.SECONDS);

既然我有这样的需求,那就Google一下吧,找了大半圈,愣是没找到相关资料,都是一些关于Java线程池的深入分析。或者是全局变量啥的,并没有找到令楼主满意的解决方案。

既然没有线程的那就扒一下scheduleAtFixedRate的底层源码看看是什么东西吧,果不其然我在源码中看到了scheduleAtFixedRate方法的具体实现,发现他的返回值是ScheduledFuture。

public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
                         long initialDelay,
                         long period,
                         TimeUnit unit) {
    if (command == null || unit == null)
      throw new NullPointerException();
    if (period <= 0)
      throw new IllegalArgumentException();
    ScheduledFutureTask<Void> sft =
      new ScheduledFutureTask<Void>(command,
                     null,
                     triggerTime(initialDelay, unit),
                     unit.toNanos(period));
    RunnableScheduledFuture<Void> t = decorateTask(command, sft);
    sft.outerTask = t;
    delayedExecute(t);
    return t;
  }

接着往下我们再看看ScheduledFuture里面有什么东西吧,没有让楼主失望,看到了这个

public boolean cancel(boolean mayInterruptIfRunning) {
      boolean cancelled = super.cancel(mayInterruptIfRunning);
      if (cancelled && removeOnCancel && heapIndex >= 0)
        remove(this);
      return cancelled;
}

//从线程的运行队列中移除当前线程
public boolean remove(Runnable task) {
    boolean removed = workQueue.remove(task);
    tryTerminate(); // In case SHUTDOWN and now empty
    return removed;
}

再往上查super.cancel(mayInterruptIfRunning)是什么东西,我们看到了这个,

//通过调用线程的interrupt方法终止线程运行
public boolean cancel(boolean mayInterruptIfRunning) {
    if (!(state == NEW &&
       UNSAFE.compareAndSwapInt(this, stateOffset, NEW,
         mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
      return false;
    try {  // in case call to interrupt throws exception
      if (mayInterruptIfRunning) {
        try {
          Thread t = runner;
          if (t != null)
            t.interrupt();
        } finally { // final state
          UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
        }
      }
    } finally {
      finishCompletion();
    }
    return true;
  }

到这里所有的问题都迎刃而解。

总结一下吧

项目中总是会遇到比较难搞的解决方案,当Google不太好找时,翻一下jdk的源码或许也是一个不错的方法。

您可能感兴趣的文章:

  • java编写属于自己的线程池
  • Java手写线程池的实现方法
  • java简单实现多线程及线程池实例详解
  • Java并发之串行线程池实例解析
  • Java ExecutorService四种线程池使用详解
  • Java线程池FutureTask实现原理详解
  • java线程池工作队列饱和策略代码示例
  • 深入理解Java编程线程池的实现原理
  • Java线程池使用与原理详解
  • java多线程学习笔记之自定义线程池
(0)

相关推荐

  • Java并发之串行线程池实例解析

    前言 做Android的这两年时间,通过研究Android源码,也会Java并发处理多线程有了自己的一些理解. 那么问题来了,如何实现一个串行的线程池呢? 思路 何为串行线程池呢? 也就是说,我们的Runnable对象应该有个排队的机制,它们顺序从队列尾部进入,并且从队列头部选择Runnable进行执行. 既然我们有了思路,那我们就考虑一下所需要的数据结构? 既然是从队列尾部插入Runnable对象,从队列头部执行Runnable对象,我们自然需要一个队列.Java的SDK已经给我们提供了很好的

  • java线程池工作队列饱和策略代码示例

    线程池(Thread Pool) 是并行执行任务收集的实用工具.随着 CPU 引入适合于应用程序并行化的多核体系结构,线程池的作用正日益显现.通过 ThreadPoolExecutor类及其他辅助类,Java 5 引入了这一框架,作为新的并发支持部分. ThreadPoolExecutor框架灵活且功能强大,它支持特定于用户的配置并提供了相关的挂钩(hook)和饱和策略来处理满队列 Java线程池会将提交的任务先置于工作队列中,在从工作队列中获取(SynchronousQueue直接由生产者提交

  • Java ExecutorService四种线程池使用详解

    1.引言 合理利用线程池能够带来三个好处.第一:降低资源消耗.通过重复利用已创建的线程降低线程创建和销毁造成的消耗.第二:提高响应速度.当任务到达时,任务可以不需要的等到线程创建就能立即执行.第三:提高线程的可管理性.线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控.但是要做到合理的利用线程池,必须对其原理了如指掌. 2.线程池使用 Executors提供的四种线程 1.newCachedThreadPool创建一个可缓存线程池

  • Java手写线程池的实现方法

    本文实例为大家分享了Java手写线程池的实现代码,供大家参考,具体内容如下 1.线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务.线程池线程都是后台线程. 2.线程池简易架构 3.简易线程池代码(自行优化) import java.util.List; /** * 线程接口 * * @Author yjian * @Date 14:49 2017/10/14 **/ public interface IThreadPool { //加入任务 void ex

  • java多线程学习笔记之自定义线程池

    当我们使用 线程池的时候,可以使用 newCachedThreadPool()或者 newFixedThreadPool(int)等方法,其实我们深入到这些方法里面,就可以看到它们的是实现方式是这样的. public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueu

  • java简单实现多线程及线程池实例详解

    本文为大家分享了java多线程的简单实现及线程池实例,供大家参考,具体内容如下 一.多线程的两种实现方式 1.继承Thread类的多线程 /** * 继承Thread类的多线程简单实现 */ public class extThread extends Thread { public void run(){ for(int i=0;i<100;i++){ System.out.println(getName()+"-"+i); } } public static void mai

  • Java线程池FutureTask实现原理详解

    前言 线程池可以并发执行多个任务,有些时候,我们可能想要跟踪任务的执行结果,甚至在一定时间内,如果任务没有执行完成,我们可能还想要取消任务的执行,为了支持这一特性,ThreadPoolExecutor提供了 FutureTask 用于追踪任务的执行和取消.本篇介绍FutureTask的实现原理. 类视图 为了更好的理解FutureTask的实现原理,这里先提供几个重要接口和类的结构,如下图所示: RunnableAdapter ThreadPoolExecutor提供了submit接口用于提交任

  • Java线程池使用与原理详解

    线程池是什么? 我们可以利用java很容易创建一个新线程,同时操作系统创建一个线程也是一笔不小的开销.所以基于线程的复用,就提出了线程池的概念,我们使用线程池创建出若干个线程,执行完一个任务后,该线程会存在一段时间(用户可以设定空闲线程的存活时间,后面会介绍),等到新任务来的时候就直接复用这个空闲线程,这样就省去了创建.销毁线程损耗.当然空闲线程也会是一种资源的浪费(所有才有空闲线程存活时间的限制),但总比频繁的创建销毁线程好太多. 下面是我的测试代码 /* * @TODO 线程池测试 */ @

  • 深入理解Java编程线程池的实现原理

    在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间. 那么有没有一种办法使得线程可以复用,就是执行完一个任务,并不被销毁,而是可以继续执行其他的任务? 在Java中可以通过线程池来达到这样的效果.今天我们就来详细讲解一下Java的线程池,首先我们从最核心的ThreadPoolExecutor类中的方法讲起,

  • java编写属于自己的线程池

    什么是线程池 线程池就是以一个或多个线程[循环执行]多个应用逻辑的线程集合. 一般而言,线程池有以下几个部分: 完成主要任务的一个或多个线程. 用于调度管理的管理线程. 要求执行的任务队列. 线程池的作用: 线程池作用就是限制系统中执行线程的数量. 根据系统的环境情况,可以自动或手动设置线程数量,达到运行的最佳效果:少了浪费了系统资源,多了造成系统拥挤效率不高.用线程池控制线程数量,其他线程排队等候.一个任务执行完毕,再从队列的中取最前面的任务开始执行.若队列中没有等待进程,线程池的这一资源处于

随机推荐