分布式锁-yudao之设计精妙之处

芋道源码ruoyi-vue-pro

源码地址:gitee

官方文档

B站视频教程

分布式锁的实现开始

/**
 * 支付通知的锁 Redis DAO
 *
 * @author 芋道源码
 */
@Repository
public class PayNotifyLockRedisDAO {

    @Resource
    private RedissonClient redissonClient;

    public void lock(Long id, Long timeoutMillis, Runnable runnable) {
        // 获取到当前的redis中的唯一key(pay_notify:lock:支付id)
        String lockKey = formatKey(id);
        // 获取锁
        RLock lock = redissonClient.getLock(lockKey);
        try {
            // 加锁并获取超时时间
            lock.lock(timeoutMillis, TimeUnit.MILLISECONDS);
            // 执行逻辑
            runnable.run();
        } finally {
            // 释放锁
            lock.unlock();
        }
    }
    private static String formatKey(Long id) {
        // 包装成 :pay_notify:lock:支付id 返回
        return String.format(PAY_NOTIFY_LOCK.getKeyTemplate(), id);
    }

精妙:加锁payNotifyLockCoreRedisDAO.lock()具体的业务逻辑通过:runnable.run(); 他来执行

业务层的方法调用

 /**
     * 同步执行单个支付通知
     *
     * @param task 通知任务
     */
    public void executeNotifySync(PayNotifyTaskDO task) {
        // 分布式锁,避免并发问题
        payNotifyLockCoreRedisDAO.lock(task.getId(), NOTIFY_TIMEOUT_MILLIS, () -> {
            // 校验,当前任务是否已经被通知过
            // 虽然已经通过分布式加锁,但是可能同时满足通知的条件,然后都去获得锁。此时,第一个执行完后,第二个还是能拿到锁,然后会再执行一次。
            PayNotifyTaskDO dbTask = payNotifyTaskCoreMapper.selectById(task.getId());
            if (DateUtils.afterNow(dbTask.getNextNotifyTime())) {
                log.info("[executeNotify][dbTask({}) 任务被忽略,原因是未到达下次通知时间,可能是因为并发执行了]", JsonUtils.toJsonString(dbTask));
                return;
            }

            // 执行通知
            executeNotify(dbTask);
        });
    }

给一个runnable 的执行 :也就是业务逻辑。

在给大家介绍一个比较常用的JUC类

 @Override
    public int executeNotify() throws InterruptedException {
        // 获得需要通知的任务
        List<PayNotifyTaskDO> tasks = payNotifyTaskCoreMapper.selectListByNotify();
        if (CollUtil.isEmpty(tasks)) {
            return 0;
        }

        // 遍历,逐个通知
        CountDownLatch latch = new CountDownLatch(tasks.size());
        tasks.forEach(task -> threadPoolTaskExecutor.execute(() -> {
            try {
                executeNotifySync(task);
            } finally {
                latch.countDown();
            }
        }));
        // 等待完成
        awaitExecuteNotify(latch);
        // 返回执行完成的任务数(成功 + 失败)
        return tasks.size();
    }

CountDownLatch latch.countDown();减少锁存器的计数,如果计数达到零,则释放所有等待的线程。
如果当前计数大于零,则递减。如果新计数为零,则出于线程调度目的,将重新启用所有等待线程。
如果当前计数等于零,则不会发生任何事情。
awaitExecuteNotify(latch):等待全部支付通知的完成 每 1 秒会打印一次剩余任务数量
latch.await(1L, TimeUnit.SECONDS) 一秒后唤醒新的线程 一般不设置

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SteveCode.

永远年轻,永远热泪盈眶

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值