基本介绍

ScheduledExecutorService 是 Java 中用于调度任务的执行器服务,它允许在给定的延迟后或定期执行任务。相比于 TimerTimerTaskScheduledExecutorService 提供了更灵活和强大的功能,并能更好地处理多线程环境下的任务调度。

核心方法

ScheduledExecutorService 提供了以下核心方法:

  1. schedule(Runnable command, long delay, TimeUnit unit):
    在给定的延迟后执行一次任务。
  2. schedule(Callable callable, long delay, TimeUnit unit):
    在给定的延迟后执行一次任务,并返回一个结果。
  3. scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit):
    从给定的初始延迟后开始,每隔指定的时间段执行一次任务。
  4. scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit):
    从给定的初始延迟后开始,每次任务执行完成后,等待指定的延迟再执行下一次任务。

使用示例

以下是一个使用 ScheduledExecutorService 的示例,展示了如何定期生成令牌并消费令牌。

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class TokenBucket {

    private final int capacity;  // 桶的容量
    private final int tokensPerSecond;  // 每秒生成的令牌数
    private int tokens;  // 当前令牌数
    private final ScheduledExecutorService scheduler;

    public TokenBucket(int tokensPerSecond, int capacity) {
        this.capacity = capacity;
        this.tokensPerSecond = tokensPerSecond;
        this.tokens = 0;
        this.scheduler = Executors.newScheduledThreadPool(1);
        startTokenGeneration();
    }

    // 定期生成令牌
    private void startTokenGeneration() {
        scheduler.scheduleAtFixedRate(() -> {
            synchronized (this) {
                tokens = Math.min(capacity, tokens + tokensPerSecond);
                System.out.println("Tokens generated: " + tokens);
            }
        }, 0, 1, TimeUnit.SECONDS);
    }

    // 消耗令牌
    public synchronized boolean consume(int numTokens) {
        if (tokens >= numTokens) {
            tokens -= numTokens;
            return true;
        }
        return false;
    }

    public static void main(String[] args) throws InterruptedException {
        TokenBucket bucket = new TokenBucket(1, 10);

        Runnable sendPacket = () -> {
            int packetSize = 1;
            if (bucket.consume(packetSize)) {
                System.out.println("Packet of size " + packetSize + " sent.");
            } else {
                System.out.println("Packet of size " + packetSize + " dropped.");
            }
        };

        ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
        executor.scheduleAtFixedRate(sendPacket, 0, 100, TimeUnit.MILLISECONDS);

        // 运行10秒钟
        Thread.sleep(10000);
        executor.shutdown();
        bucket.scheduler.shutdown();
    }
}

代码说明

  • TokenBucket 类
    • capacity:令牌桶的最大容量。
    • tokensPerSecond:每秒生成的令牌数。
    • tokens:当前令牌数。
    • scheduler:用于定期生成令牌的调度器。
  • startTokenGeneration 方法:使用 scheduleAtFixedRate 每秒生成一次令牌,更新令牌数。
  • consume 方法:尝试消耗指定数量的令牌,如果令牌数足够则消耗并返回 true,否则返回 false
  • main 方法:创建一个令牌桶实例,定期尝试发送数据包,并在控制台输出发送结果。

应用场景

  • 定时任务执行:定期执行某些任务,如定时备份、日志清理等。
  • 周期性任务调度:如定时刷新缓存、定时发送心跳包等。
  • 延迟任务执行:在某个延迟后执行一次任务。

优缺点

优点

  • 强大的定时功能:相比于 TimerScheduledExecutorService 提供了更强大和灵活的定时任务执行能力。
  • 多线程支持:能够更好地处理多线程环境下的任务调度。
  • 灵活性高:可以轻松实现延迟执行、定期执行等各种任务调度需求。

缺点

  • 复杂性:相比于 TimerScheduledExecutorService 的使用略显复杂。
  • 资源消耗:需要管理线程池,可能会带来额外的资源消耗。

面试问答

问:什么是 ScheduledExecutorService
答:ScheduledExecutorService 是 Java 中用于调度任务的执行器服务,它允许在给定的延迟后或定期执行任务。
问:如何使用 ScheduledExecutorService 实现定期任务?
答:可以使用 scheduleAtFixedRate 方法来实现定期任务调度,或者使用 scheduleWithFixedDelay 方法来实现任务完成后的延迟调度。
问:ScheduledExecutorService** 和 Timer 有什么区别?**
答:ScheduledExecutorService 提供了更灵活和强大的功能,能够更好地处理多线程环境下的任务调度,而 Timer 相对简单,但在多线程环境下的表现较差。
问:如何优雅地关闭 ScheduledExecutorService
答:可以使用 shutdown 方法来关闭 ScheduledExecutorService,它会停止接受新任务,并等待已经提交的任务执行完成。可以使用 shutdownNow 方法立即停止所有任务。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐