超时hsy

好,这一步我们在你刚刚那套“100% 并发安全”的最终版之上只加两件事

✅ 超时
✅ 安全抢占
❌ 不引入 JVM 锁
❌ 不引入新表(还是 concurrent_ctrl)
❌ 不破坏“异步线程池 + token 释放”这个正确模型

我会给你一套生产级、逻辑闭环、不会出现你之前担心的混乱场景的实现。


一、核心思想(先给结论)

超时 ≠ 直接释放锁
抢占 ≠ 无条件覆盖

必须满足 三要素

  1. 锁有唯一 token(任务身份)
  2. 抢占是 CAS(条件更新)
  3. 释放必须校验 token

只要这三点在,永远不会出现“任务 A 释放任务 B 的锁”


二、表结构(在你已有表上最小扩展)

CREATE TABLE concurrent_ctrl (
    biz_key      VARCHAR(64) PRIMARY KEY,
    token        VARCHAR(64) NOT NULL,
    expire_time  BIGINT      NOT NULL
);

说明:

  • biz_key:如 IMPORT_EXPORT
  • token:UUID,标识任务
  • expire_time:毫秒时间戳(锁的“租约”)

三、TaskConControl(核心逻辑)

1️⃣ 加锁(含超时 + 抢占)

@Component
public class TaskConControl {

    private static final long TIMEOUT_MS = 30 * 60 * 1000L; // 30 分钟

    @Autowired
    private ConcurrentCtrlDao dao;

    public ConcurrentCtrl lock() {
        long now = System.currentTimeMillis();

        ConcurrentCtrl lock = new ConcurrentCtrl();
        lock.setBizKey("IMPORT_EXPORT");
        lock.setToken(UUID.randomUUID().toString());
        lock.setExpireTime(now + TIMEOUT_MS);

        // ① 先尝试直接插入(无锁)
        try {
            dao.insert(lock);
            return lock;
        } catch (DuplicateKeyException ignore) {
            // 已存在,走下面逻辑
        }

        // ② 已存在锁,尝试“超时抢占”
        ConcurrentCtrl exist = dao.selectForUpdate("IMPORT_EXPORT");

        if (exist == null) {
            // 理论上极少发生,兜底
            dao.insert(lock);
            return lock;
        }

        // ③ 未超时 → 直接失败
        if (exist.getExpireTime() > now) {
            throw new RuntimeException("已有任务正在执行");
        }

        // ④ 已超时 → CAS 抢占
        int updated = dao.casUpdate(
                lock.getBizKey(),
                exist.getToken(),
                lock.getToken(),
                lock.getExpireTime()
        );

        if (updated == 1) {
            return lock;
        }

        // ⑤ 抢占失败 → 并发下被别人抢走
        throw new RuntimeException("已有任务正在执行");
    }

    /**
     * 释放锁(只能释放自己的)
     */
    public void unlock(ConcurrentCtrl lock) {
        if (lock == null) return;

        dao.deleteByBizKeyAndToken(
                lock.getBizKey(),
                lock.getToken()
        );
    }
}

2️⃣ DAO(关键 SQL)

select(用于判断是否超时)

SELECT biz_key, token, expire_time
FROM concurrent_ctrl
WHERE biz_key = #{bizKey}

CAS 抢占(安全核心

UPDATE concurrent_ctrl
SET token = #{newToken},
    expire_time = #{newExpireTime}
WHERE biz_key = #{bizKey}
  AND token   = #{oldToken}

⚠️ 没有 oldToken = token 这一条件,整个方案就是不安全的


释放(带 token)

DELETE FROM concurrent_ctrl
WHERE biz_key = #{bizKey}
  AND token   = #{token}

四、EndpointImpl(几乎不变)

@Component
public class EndpointImpl {

    @Autowired
    private TaskConControl taskConControl;

    @Autowired
    private ExecutorService executorService;

    public void exportData() {
        ConcurrentCtrl lock;
        try {
            lock = taskConControl.lock();
        } catch (Exception e) {
            return;
        }

        executorService.submit(() -> {
            try {
                // do biz
            } finally {
                taskConControl.unlock(lock);
            }
        });
    }

    public void importData() {
        ConcurrentCtrl lock;
        try {
            lock = taskConControl.lock();
        } catch (Exception e) {
            return;
        }

        executorService.submit(() -> {
            try {
                // do biz
            } finally {
                taskConControl.unlock(lock);
            }
        });
    }
}

五、用你最担心的“32 分钟场景”验证一次

场景:

  • 任务 A:执行 32 分钟
  • 超时:30 分钟

时间线:

T0
A 加锁

token=A, expire=30

T30
B 请求 → 超时 → CAS 抢占

token=B, expire=60

T32
A 执行完 → unlock(A)

DELETE ... WHERE token = A

👉 0 行影响,不会删 B 的锁

✔️ 安全


六、为什么不需要定时任务“强制解锁”

  • 锁是 租约
  • 超时后自然可被抢占
  • 不存在“误释放”

如果你愿意,可以加一个清理任务

DELETE FROM concurrent_ctrl
WHERE expire_time < now() - 24h

仅做垃圾清理,不影响正确性。


七、这套方案你已经走到哪一步了?

你现在已经实现的是:

能力 是否具备
多实例并发控制
异步安全
超时恢复
安全抢占
不误删

八、最后一句(架构级总结)

这是一个“带租约的分布式互斥锁”
思路和:

  • Redis RedLock
  • Zookeeper 临时节点
  • Quartz Scheduler
    同一类模型

如果你愿意,下一步我可以帮你:

  • 🔁 续租机制(长任务心跳)
  • 📊 任务状态表 + 可观测
  • 🚦 从“拒绝”升级为“排队执行”
  • 🧪 并发/极端场景完整推演

你选一个,我继续往下给。

posted @ 2026-01-26 01:17  何人可  阅读(3)  评论(0)    收藏  举报