spring定时任务如何防止重复调用

在Spring框架中,我们可以使用@Scheduled注解来创建定时任务,如果没有正确地处理重复调用的问题,可能会导致任务被多次执行,从而产生不必要的开销和错误,如何防止Spring定时任务的重复调用呢?本文将详细介绍几种防止Spring定时任务重复调用的方法。

1、单例模式

spring定时任务如何防止重复调用

我们可以使用单例模式来确保定时任务只被执行一次,单例模式是一种设计模式,它保证一个类只有一个实例,并提供一个全局访问点,在Spring中,我们可以使用@Component注解将定时任务类声明为一个Bean,这样Spring容器就会为我们创建一个单例对象,我们可以在这个单例对象中定义定时任务方法,并使用@Scheduled注解来指定任务的执行频率。

import org.springframework.stereotype.Component;
import org.springframework.scheduling.annotation.Scheduled;
@Component
public class MyTask {
    private boolean isRunning = false;
    @Scheduled(fixedRate = 1000)
    public void execute() {
        if (!isRunning) {
            isRunning = true;
            // 执行任务逻辑
            System.out.println("任务执行中...");
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                isRunning = false;
            }
        } else {
            System.out.println("任务正在执行中,跳过本次执行");
        }
    }
}

2、使用数据库锁

spring定时任务如何防止重复调用

另一种防止定时任务重复调用的方法是使用数据库锁,我们可以在任务执行前查询数据库中的某个表,如果表中没有记录,则表示任务还没有被执行,可以继续执行;否则,表示任务已经在执行中,需要跳过本次执行,在任务执行完成后,我们需要更新数据库中的记录,以释放锁,这种方法适用于多个服务器部署的情况,因为每个服务器都有自己的数据库实例。

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
public class MyTask {
    private final JdbcTemplate jdbcTemplate;
    private static final String LOCK_TABLE = "lock_table";
    private static final String LOCK_COLUMN = "lock_column";
    private static final String LOCK_VALUE = "lock_value";
    private static final int LOCK_EXPIRE_TIME = 60000; // 锁过期时间,单位:毫秒
    public MyTask(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }
    @Scheduled(fixedRate = 1000)
    public void execute() {
        if (!isLocked()) {
            // 获取锁并执行任务逻辑
            acquireLockAndExecute();
        } else {
            System.out.println("任务正在执行中,跳过本次执行");
        }
    }
    private boolean isLocked() {
        return jdbcTemplate.queryForObject("SELECT COUNT(*) FROM " + LOCK_TABLE + " WHERE " + LOCK_COLUMN + " = '" + LOCK_VALUE + "'", Integer.class) > 0;
    }
    private void acquireLockAndExecute() {
        // 更新锁表记录,设置过期时间
        jdbcTemplate.update("INSERT INTO " + LOCK_TABLE + " (id, " + LOCK_COLUMN + ", expire_time) VALUES (1, '" + LOCK_VALUE + "', " + System.currentTimeMillis() + " + " + LOCK_EXPIRE_TIME + ") ON DUPLICATE KEY UPDATE expire_time = " + System.currentTimeMillis() + " + " + LOCK_EXPIRE_TIME);
        // 执行任务逻辑
        System.out.println("任务执行中...");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // 释放锁表记录的锁
            jdbcTemplate.update("DELETE FROM " + LOCK_TABLE + " WHERE id = 1");
        }
    }
}

3、使用分布式锁服务(如Redis)

spring定时任务如何防止重复调用

除了使用数据库锁外,我们还可以使用分布式锁服务(如Redis)来防止定时任务的重复调用,分布式锁是一种跨多个服务器的锁,它可以确保在多个服务器上运行的同一任务不会被同时执行,我们可以使用Redis的命令SETNX来实现分布式锁,当任务开始执行时,我们尝试使用SETNX命令在Redis中设置一个锁;如果设置成功,表示我们获得了锁,可以继续执行任务;否则,表示锁已经被其他进程持有,我们需要等待一段时间后再次尝试获取锁,在任务执行完成后,我们需要使用DEL命令来释放锁,这种方法适用于多个服务器部署的情况,因为每个服务器都可以访问同一个Redis实例。

原创文章,作者:K-seo,如若转载,请注明出处:https://www.kdun.cn/ask/250643.html

(0)
K-seoK-seoSEO优化员
上一篇 2024年1月23日 13:57
下一篇 2024年1月23日 13:57

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

免备案 高防CDN 无视CC/DDOS攻击 限时秒杀,10元即可体验  (专业解决各类攻击)>>点击进入