Java 定时任务
一、 使用 while(true) 和 sleep 实现
new Thread(){ @Override public void run() { while (true) { System.out.println("Hello!"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start();
二、使用 Timer 和 TimerTask 实现
/** * 一个 Timer 对应一个线程,用于执行任务 * @param name 线程名字 * @param isDaemon 是否为当前线程的守护线程 */ Timer timer = new Timer("haha", false); // 一个 TimerTask 对应一个任务 TimerTask task1 = new TimerTask() { @Override public void run() { System.out.println(Thread.currentThread().getName() + "Hello! A"); } }; // 立即执行,只执行一次 timer.schedule(task1, 0); timer.schedule(task1, 0, 1000); TimerTask task2 = new TimerTask() { @Override public void run() { System.out.println(Thread.currentThread().getName() + "Hello! B"); } }; // 立即执行,然后每秒执行一次 timer.scheduleAtFixedRate(task2, 0, 1000); Thread.sleep(5000); // 取消任务 task2.cancel(); Thread.sleep(5000); // 将所有已经取消的任务移除(释放资源) timer.purge(); // 停止 timer 中的所有任务,并终止计时器,无法再提交任务 timer.cancel();
schedule 与 scheduleAtFixedRate 区别
schedule 注重间隔时间,不管任务执行需要多长时间,下一次执行都是在执行完成后的指定间隔时间再执行。
scheduleAtFixedRate 注重执行次数,例如当任务太多或其他原因导致某段时间内执行次数不够(总时间/间隔时间),则会尝试缩短间隔时间,保证总体执行次数。
三、使用 ScheduledThreadPoolExecutor 实现
阿里巴巴 Java 手册中的片段
多线程并行处理定时任务时,Timer 运行多个 TimeTask 时,只要其中之一没有捕获抛出的异常,其它任务便会自动终止运行,使用 ScheduledExecutorService 则没有这个问题。 线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。 说明:Executors 返回的线程池对象的弊端如下: 1)FixedThreadPool 和 SingleThreadPool: 允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。 2)CachedThreadPool 和 ScheduledThreadPool: 允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。
说明了创建线程池的规范,以及使用 Timer 的问题
// Guava 库的工具类,线程工厂,这里主要用来设置线程名字 ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("demo-pool-%d").build(); ScheduledThreadPoolExecutor stpe = new ScheduledThreadPoolExecutor(10, namedThreadFactory, new ThreadPoolExecutor.AbortPolicy()); // 一秒后执行 stpe.schedule(() -> { System.out.println(Thread.currentThread().getName() + "\tA"); }, 1, TimeUnit.SECONDS); // 立即执行,然后每秒执行一次,注重次数 stpe.scheduleAtFixedRate(() -> { System.out.println(Thread.currentThread().getName() + "\tB"); }, 0, 1, TimeUnit.SECONDS); // 立即执行,然后每两秒执行一次,注重间隔时间 stpe.scheduleWithFixedDelay(() -> { System.out.println(Thread.currentThread().getName() + "\tC"); }, 0, 2, TimeUnit.SECONDS); Thread.sleep(10000); stpe.purge(); // 关闭 stpe.shutdown();