首页 > 自考资讯 > 自考知识

java定时器配置,java 定时任务配置

头条共创 2024-07-05

我们知道Java中的定时调度可以通过TimerTimerTask来实现。由于实现是单线程的,所以从JDK1.3发布以来,出现了几个问题,大致如下:

多个任务相互影响。 ScheduledExecutorService最初是为了解决TimerTimerTask的问题而设计的。它本质上是基于多线程机制,因此任务之间不会互相影响(只要线程数量足够;否则,某些任务会复用同一个线程)。

另外,内部使用的延迟队列本身是基于等待/唤醒机制实现的,因此CPU并不总是忙碌的。同时,通过多线程对CPU资源的复用也显着提升了性能。

如何使用

基本功能

ScheduledExecutorService继承自ExecutorService,因此它支持线程池的所有功能。提供了四种附加方法。我们来看看它的特点。

/** * 带延迟的调度,只执行一次* 调度后,可以使用Future.get() 阻塞,直到任务完成*/1. /** * 带延迟时间的调度,只执行一次。 * 调度完成后,可以使用Future.get()进行阻塞,直到任务执行完成并获取执行结果。 */2. 可调用,长延迟,以TimeUnit 为单位); /** * 带延迟时间的调度,定期执行,固定频率*/3. /** * ScheduledFuture 周期性执行,固定延迟*/4.

该方法用于带延迟时间的调度,仅执行一次。调度完成后,可以使用Future.get() 进行阻塞,直到任务完成。让我们看一个例子。

@Test public void test_schedule4Runnable() 抛出异常{ ScheduledExecutorService service=Executors.newSingleThreadScheduledExecutor(); ScheduledFuture future=service.schedule(() - { try { Thread.sleep(3000L); } catch (InterruptedException e) { e.printStackTrace( ); } System.out.println('任务结束时间: ' + format(System.currentTimeMillis())) }, 1000, TimeUnit.MILLISECONDS); System.out.println('计划结束时间: ' + format(System .currentTimeMillis())); System.out.println('Runnable future's result is: ' + future.get() + ', and time is: ' + format(System.currentTimeMillis()));} 通过上述代码实现效果如下。延迟执行时间为1秒,任务执行3秒,任务只运行一次,并且使用Future.get()阻塞直到任务执行完成。

如下图所示,结果完全符合预期。

5b13f569b5fe4b1480e27a369854cb9c~noop.image?_iz=58558&from=article.pc_detail&lk3s=953192f4&x-expires=1720775326&x-signature=vqNPYyA6Qc%2F5E1Fpuk0PiQgJbiQ%3D2 可安排通话

根据调度Runnable,我们将Runnable改为Callable看一下。

@Test public void test_schedule4Callable() 抛出异常{ ScheduledExecutorService service=Executors.newSingleThreadScheduledExecutor(); ScheduledFutureString future=service.schedule(() - { try { Thread.sleep(3000L); } catch (InterruptedException e) { e.printStackTrace( ); } System.out.println('任务结束时间: ' + format(System.currentTimeMillis())); '成功' }, 1000, TimeUnit.MILLISECONDS) + format(System.currentTimeMillis()));得到的结果基本和Runnable一样。唯一的区别是future.get() 可以检索Callable 返回的实际结果。

97569d16d59a40339fbb3f75b2301730~noop.image?_iz=58558&from=article.pc_detail&lk3s=953192f4&x-expires=1720775326&x-signature=7TmvmKfrSA%2FPVN%2BkgTwKu%2FtDroU%3D3。固定费率表

该方法用于以固定频率运行任务循环。我们通过一个例子来看看它的效果。

@Test public void test_scheduleAtFixedRate() { ScheduledExecutorService service=Executors.newScheduledThreadPool(5); service.scheduleAtFixedRate(() - { try { Thread.sleep(3000L); } catch (InterruptedException e) { e.printStackTrace(); } System .out.println('任务结束时间: ' + format(System.currentTimeMillis())) }, 1000L, 1000L, TimeUnit.MILLISECONDS); System.out.println('计划结束时间: ' + format(System.currentTimeMillis); ) () ))); while (true) { }} 在本例中,任务的初始延迟为1 秒,任务运行3 秒,任务运行之间的间隔为1 秒。我们来看看执行结果。

61a5c2586fcc41aa81edc8178f63b186~noop.image?_iz=58558&from=article.pc_detail&lk3s=953192f4&x-expires=1720775326&x-signature=P5yOd%2BUGZ591wdIzXaduYl4wZdI%3D4。

此方法用于以固定延迟运行任务循环。我们通过一个例子看看它的效果。

@Test public void test_scheduleWithFixedDelay() { ScheduledExecutorService service=Executors.newScheduledThreadPool(5); service.scheduleWithFixedDelay(() - { try { Thread.sleep(3000L); } catch (InterruptedException e) { e.printStackTrace(); } System .out.println('任务结束时间: ' + format(System.currentTimeMillis())) }, 1000L, 1000L, TimeUnit.MILLISECONDS); System.out.println('计划结束时间: ' + format(System.currentTimeMillis); ) () ))); while (true) { }} 在本例中,任务的初始延迟为1 秒,任务运行3 秒,任务运行之间的间隔为1 秒。我们来看看执行结果。

adb74692873b4245b933dd26ad65997d~noop.image?_iz=58558&from=article.pc_detail&lk3s=953192f4&x-expires=1720775326&x-signature=49Ueis5xeRr%2BcTDEMPYvH7CCafk%3D5。 ScheduleAtFixedRate 和ScheduleWithFixedDelay 之间的区别

这两种方法都是周期性执行任务,但是它们有什么区别呢?我们通过jdk文档找到了答案。

2f9c068fb26440cc893ee86f5005a66c~noop.image?_iz=58558&from=article.pc_detail&lk3s=953192f4&x-expires=1720775326&x-signature=%2Bk4mgs7VopNJAnh4w7yOnb9Zj0E%3D adf903ed3a4146a6b3c1206ae2245fc9~noop.image?_iz=58558&from=article.pc_detail&lk3s=953192f4&x-expires=1720775326&x-signature=yPlNSwH%2BztjFOd7wq5%2FR8%2F36tNA%3D 坦白来说,scheduleAtFixedRate()是固定频率,scheduleWithFixedDelay()是固定延迟。固定频率是相对于任务执行的开始时间而言的,固定延迟是相对于任务执行的结束时间而言的。这是最基本的区别。

从3和4的运行结果也可以看出这些差异。

阅读源码初体验

一般来说,源代码的入口点是构造方法。让我们来看看。

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) { return new ScheduledThreadPoolExecutor(corePoolSize);} public ScheduledThreadPoolExecutor(int corePoolSize) { super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue());} 构造函数在术语中,它看起来像这样:信息:

ScheduledThreadPoolExecutor构造函数最终调用ThreadPoolExecutor构造函数,阻塞队列使用DelayedWorkQueue。上述信息中的第2点很重要,但由于篇幅限制,本文不会对其进行详细分析。

接下来我们看一下scheduleWithFixedDelay()方法。这个方法主要做了三件事。

验证输入参数,包括空指针和数值范围。将其包装在RunnableScheduledFuture 中以延迟其执行。 public ScheduledFuture? Range if (command==null || Unit==null) throw new NullPointerException(); throw new IllegalArgumentException() //2. 将Runnable 包装在`RunnableScheduledFuture` ScheduledFutureTaskVoid sft=new ScheduledFutureTaskVoid 中(command) , null,triggerTime(initialDelay,unit),unit.toNanos(-lay)); RunnableScheduledFutureVoid t=decorateTask(command, sft); //3. 延迟执行`RunnableScheduledFuture` Masu. deferredexecution()方法字面意思就是延迟执行。让我们仔细看看这个方法。

private void LateExecute(RunnableScheduledFuture? task) { //1. 检查线程池的执行状态if (isShutdown())reject(task); else { //2. 将任务添加到队列super.getQueue( ). add(task) ; //3. 如果任务添加到队列后线程池状态变为非执行, //该任务必须从队列中移除并且任务的`cancel()` 必须取消该任务通过Method if (isShutdown() !canRunInCurrentRunState(task.isPeriodic())Remove(task)) task.cancel(false); //4.任务加入队列后,如果线程池状态为Running,则该线程如果线程池状态健康,最终会调用ensurePrestart()方法来完成线程创建。主要有两个逻辑。

如果当前线程数未达到核心线程数,则创建核心线程。如果当前线程数达到核心线程数,则创建非核心线程,不放置任何任务。这与常规线程池不同。 ** * 与prestartCoreThread 相同,只是它确保即使corePoolSize 为0 也至少启动一个线程。 */void EnsurePrestart() { int wc=workerCountOf(ctl.get()) ); //1.当前线程数尚未达到如果核心线程数达到核心线程数,则移除核心线程. Create if (wc corePoolSize) addWorker(null, true); //2.如果当前线程数达到核心线程数,则创建非核心线程。 //2.1 任务不放入阻塞队列。这与(wc==0) 时的常规线程池不同。 addWorker(null, false);} 至此,除了DelayedWorkQueue延迟队列的源代码之外,我们已经分析了所有内容。

总结

首先,我了解了ScheduledExecutorService的基本功能,并在此基础上创建了一个演示demo,结果与基本功能完全相同。

经过初步分析内部实现原理和源码,我们发现队列的阻塞和线程的创建方式与普通线程池不同。

版权声明:本文由今日头条转载,如有侵犯您的版权,请联系本站编辑删除。

猜你喜欢