Java调度任务如何使用分布式锁保证相同任务在一个周期里只执行一次?

简介: 【10月更文挑战第29天】Java调度任务如何使用分布式锁保证相同任务在一个周期里只执行一次?

在Java中,使用分布式锁可以确保在多个实例或服务器之间,相同任务在一个周期内只执行一次。以下是实现这一功能的步骤和示例代码:

步骤

  1. 选择分布式锁的实现:可以使用Redis、Zookeeper等来实现分布式锁。
  2. 获取锁:在任务开始执行之前尝试获取锁。如果成功获取到锁,则继续执行任务;如果没有获取到锁,则跳过执行。
  3. 释放锁:任务执行完毕后,释放锁,以便其他实例可以获取锁并执行任务。

示例代码(使用Redis作为分布式锁)

首先,添加Redis客户端依赖到你的项目中(例如使用Jedis):

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>3.6.0</version>
</dependency>

然后,创建一个使用Redis分布式锁的任务调度器:

import redis.clients.jedis.Jedis;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class DistributedTaskScheduler {
   
    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
    private final Jedis jedis = new Jedis("localhost", 6379); // 连接到Redis服务器

    public void start() {
   
        final Runnable task = () -> {
   
            String lockKey = "taskLock";
            long acquireTimeout = 5000; // 获取锁的超时时间(毫秒)
            long lockExpiry = 60000; // 锁的过期时间(毫秒)

            // 尝试获取锁
            if (acquireLock(lockKey, acquireTimeout, lockExpiry)) {
   
                try {
   
                    // 执行任务逻辑
                    System.out.println("Executing task at " + System.currentTimeMillis());
                } finally {
   
                    // 释放锁
                    releaseLock(lockKey);
                }
            } else {
   
                System.out.println("Failed to acquire lock, skipping task execution");
            }
        };
        // 每分钟检查一次是否需要执行任务
        scheduler.scheduleAtFixedRate(task, 0, 1, TimeUnit.MINUTES);
    }

    private boolean acquireLock(String lockKey, long acquireTimeout, long lockExpiry) {
   
        long end = System.currentTimeMillis() + acquireTimeout;
        while (System.currentTimeMillis() < end) {
   
            if (jedis.setnx(lockKey, "locked") == 1) {
    // 尝试设置锁
                jedis.expire(lockKey, (int) (lockExpiry / 1000)); // 设置锁的过期时间
                return true;
            }
            try {
   
                Thread.sleep(100); // 等待一段时间后重试
            } catch (InterruptedException e) {
   
                Thread.currentThread().interrupt();
            }
        }
        return false;
    }

    private void releaseLock(String lockKey) {
   
        jedis.del(lockKey); // 删除锁
    }

    public static void main(String[] args) {
   
        DistributedTaskScheduler scheduler = new DistributedTaskScheduler();
        scheduler.start();
    }
}

在这个例子中,我们使用了Redis来存储锁的状态。acquireLock方法尝试获取锁,并在成功获取后设置一个过期时间,以防止死锁的情况发生。releaseLock方法用于释放锁。通过这种方式,我们可以确保在多个实例中,同一任务在一个周期内只被执行一次。

目录
相关文章
|
1月前
|
数据采集 缓存 Java
Python vs Java:爬虫任务中的效率比较
Python vs Java:爬虫任务中的效率比较
|
13天前
|
NoSQL Java 调度
Java调度任务如何保证相同任务在一个周期里只执行一次?
【10月更文挑战第29天】Java调度任务如何保证相同任务在一个周期里只执行一次?
43 6
|
1月前
|
NoSQL Java 数据库
Java分布式锁
Java分布式锁
37 0
|
1月前
|
缓存 Java 数据库
JAVA分布式CAP原则
JAVA分布式CAP原则
49 0
|
分布式计算 并行计算 算法
Java7任务并行执行神器:Fork&Join框架
Fork/Join是什么? Fork/Join框架是Java7提供的并行执行任务框架,思想是将大任务分解成小任务,然后小任务又可以继续分解,然后每个小任务分别计算出结果再合并起来,最后将汇总的结果作为大任务结果。其思想和MapReduce的思想非常类似。对于任务的分割,要求各个子任务之间相互独立,能够并行独立地执行任务,互相之间不影响。
264 0
Java7任务并行执行神器:Fork&Join框架
|
8天前
|
安全 Java 测试技术
Java并行流陷阱:为什么指定线程池可能是个坏主意
本文探讨了Java并行流的使用陷阱,尤其是指定线程池的问题。文章分析了并行流的设计思想,指出了指定线程池的弊端,并提供了使用CompletableFuture等替代方案。同时,介绍了Parallel Collector库在处理阻塞任务时的优势和特点。
|
17天前
|
安全 Java
java 中 i++ 到底是否线程安全?
本文通过实例探讨了 `i++` 在多线程环境下的线程安全性问题。首先,使用 100 个线程分别执行 10000 次 `i++` 操作,发现最终结果小于预期的 1000000,证明 `i++` 是线程不安全的。接着,介绍了两种解决方法:使用 `synchronized` 关键字加锁和使用 `AtomicInteger` 类。其中,`AtomicInteger` 通过 `CAS` 操作实现了高效的线程安全。最后,通过分析字节码和源码,解释了 `i++` 为何线程不安全以及 `AtomicInteger` 如何保证线程安全。
java 中 i++ 到底是否线程安全?
|
4天前
|
安全 Java 开发者
深入解读JAVA多线程:wait()、notify()、notifyAll()的奥秘
在Java多线程编程中,`wait()`、`notify()`和`notifyAll()`方法是实现线程间通信和同步的关键机制。这些方法定义在`java.lang.Object`类中,每个Java对象都可以作为线程间通信的媒介。本文将详细解析这三个方法的使用方法和最佳实践,帮助开发者更高效地进行多线程编程。 示例代码展示了如何在同步方法中使用这些方法,确保线程安全和高效的通信。
22 9
|
7天前
|
存储 安全 Java
Java多线程编程的艺术:从基础到实践####
本文深入探讨了Java多线程编程的核心概念、应用场景及其实现方式,旨在帮助开发者理解并掌握多线程编程的基本技能。文章首先概述了多线程的重要性和常见挑战,随后详细介绍了Java中创建和管理线程的两种主要方式:继承Thread类与实现Runnable接口。通过实例代码,本文展示了如何正确启动、运行及同步线程,以及如何处理线程间的通信与协作问题。最后,文章总结了多线程编程的最佳实践,为读者在实际项目中应用多线程技术提供了宝贵的参考。 ####
|
4天前
|
监控 安全 Java
Java中的多线程编程:从入门到实践####
本文将深入浅出地探讨Java多线程编程的核心概念、应用场景及实践技巧。不同于传统的摘要形式,本文将以一个简短的代码示例作为开篇,直接展示多线程的魅力,随后再详细解析其背后的原理与实现方式,旨在帮助读者快速理解并掌握Java多线程编程的基本技能。 ```java // 简单的多线程示例:创建两个线程,分别打印不同的消息 public class SimpleMultithreading { public static void main(String[] args) { Thread thread1 = new Thread(() -> System.out.prin