11.30_黑马Redis实战篇分布式锁

news2025/1/13 10:21:49

 实战篇9

设立一个在jvm外的锁监视器,可以处理多线程的问题

 

实战篇10 

 获取锁的时候,要同时发生获取锁以及设置到期时间。

实战篇11 

thinking:JAVA中的自动拆箱与装箱?

【Java基础】自动拆装箱_Elephant_King的博客-CSDN博客

 

 

 

 

 

TRUE.equals():保证不会有空指针异常。 

package com.hmdp.utils;

public interface ILock {
    /**
     * 尝试获取锁
     * @param timeoutSec 锁持有的超时时间,过期后自动释放
     * @return true代表获取锁成功;false代表获取锁失败
     */
    boolean tryLock(long timeoutSec);

    /**
     * 释放锁
     */
    void unlock();
}
package com.hmdp.utils;
import org.springframework.data.redis.core.StringRedisTemplate;
import java.util.concurrent.TimeUnit;
public class SimpleRedisLock implements ILock {
    private String name;
    private StringRedisTemplate stringRedisTemplate;
    private static final String KEY_PREFIX = "lock:";
    public SimpleRedisLock(String name, StringRedisTemplate stringRedisTemplate) {
        this.name = name;
        this.stringRedisTemplate = stringRedisTemplate;
    }
    @Override
    public boolean tryLock(long timeoutSec) {
        //获取线程标示
        long threadId = Thread.currentThread().getId();
        //释放锁
        Boolean success = stringRedisTemplate.opsForValue()
                .setIfAbsent(KEY_PREFIX + name, threadId + "", timeoutSec, TimeUnit.SECONDS);
        return Boolean.TRUE.equals(success);
    }
    @Override
    public void unlock() {
        //释放锁
        stringRedisTemplate.delete(KEY_PREFIX + name);
    }
}
package com.hmdp.service.impl;

import com.hmdp.dto.Result;
import com.hmdp.entity.SeckillVoucher;
import com.hmdp.entity.User;
import com.hmdp.entity.Voucher;
import com.hmdp.entity.VoucherOrder;
import com.hmdp.mapper.VoucherOrderMapper;
import com.hmdp.service.ISeckillVoucherService;
import com.hmdp.service.IVoucherOrderService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmdp.utils.RedisIdWorker;
import com.hmdp.utils.SimpleRedisLock;
import com.hmdp.utils.UserHolder;
import org.springframework.aop.framework.AopContext;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDateTime;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author 虎哥
 * @since 2021-12-22
 */
@Service
public class VoucherOrderServiceImpl extends ServiceImpl<VoucherOrderMapper, VoucherOrder> implements IVoucherOrderService {
    @Resource
    private ISeckillVoucherService seckillVoucherService;
    @Resource
    private RedisIdWorker redisIdWorker;
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Override
    public Result seckillVoucher(Long voucherId) {
        //1,查询优惠券
        SeckillVoucher voucher = seckillVoucherService.getById(voucherId);
        //2,判断秒杀是否开始
        if (voucher.getBeginTime().isAfter(LocalDateTime.now())) {
            //尚未开始
            return Result.fail("秒杀尚未开始!");
        }
        //3,判断秒杀是否已经结束
        if (voucher.getBeginTime().isBefore(LocalDateTime.now())) {
            //已经结束
            return Result.fail("秒杀已经结束!");
        }
        //4,判断库存是否充足
        if (voucher.getStock() < 1) {
            //库存不足
            return Result.fail("库存不足!");
        }
        //一人一锁,提高效率
        //intern 保证指定的userId对应指定的锁
        //先获取锁,再完成以下方法,先完成方法,再释放锁,才能确保线程安全
        Long userId = UserHolder.getUser().getId();
        //synchronized(userId.toString().intern()){
        //获取代理对象(事务)
        //创建锁对象
        SimpleRedisLock lock = new SimpleRedisLock("order" + userId, stringRedisTemplate);
        //获取锁
        boolean isLock = lock.tryLock(1200);
        //判断是否获取锁成功
        if(!isLock){
            //获取锁失败,返回错误或重试
            return Result.fail("不允许重复下单");
        }
        try {
            //获取代理对象(事务)
            IVoucherOrderService proxy = (IVoucherOrderService) AopContext.currentProxy();
            return proxy.createVoucherOrder(voucherId);
        } finally {
            //释放锁
            lock.unlock();
        }
        // }
    }
    //加上事务,因为这里有两张表
    @Transactional
    public  Result createVoucherOrder(Long voucherId) {
        //5,一人一单
        Long userId = UserHolder.getUser().getId();
        //5.1 查询订单
        int count = query().eq("user_id", userId).eq("voucher_id", voucherId).count();
        //5.2 判断是否存在
        if (count > 0) {
            // 用户已经购买过了
            return Result.fail("用户已经购买过一次!");
        }
        //6,扣减库存
        boolean success = seckillVoucherService
                .update().setSql("stock = stock - 1")
                .eq("voucher_id", voucherId)
                .gt("stock", 0)
                .update();
        if (!success) {
            //扣除失败
            return Result.fail("库存不足!");
        }
        //7,创建订单
        VoucherOrder voucherOrder = new VoucherOrder();
        //7.1 订单id
        long orderId = redisIdWorker.nextId("order");
        voucherOrder.setId(orderId);
        //7.2 用户id
        voucherOrder.setUserId(userId);
        //7.3 优惠券id
        voucherOrder.setVoucherId(voucherId);
        save(voucherOrder);
        //8,返回订单id
        return Result.ok(orderId);
        }
    }

thinking:我怎么知道什么时候要处理异常? 

 最全最详细的Java异常处理机制_两个系统对接 另一个系统有异常_我是波哩个波的博客-CSDN博客

实战篇12

 设置锁的标识,避免误删别人的锁,以达到避免多线程并发的情况发生

实战篇13

thinking:hutool的UUID?

【工具类用法】Hutool里的生成唯一Id唯的工具类_hutool生成uuid-CSDN博客

 thinking:java中long和string互转?

java中long和string互转_long转换string-CSDN博客

package com.hmdp.utils;
import cn.hutool.core.lang.UUID;
import org.springframework.data.redis.core.StringRedisTemplate;
import java.util.concurrent.TimeUnit;
public class SimpleRedisLock implements ILock {
    private String name;
    private StringRedisTemplate stringRedisTemplate;
    private static final String KEY_PREFIX = "lock:";
    private static final String ID_PREFIX = UUID.randomUUID().toString(true)+"-";
    public SimpleRedisLock(String name, StringRedisTemplate stringRedisTemplate) {
        this.name = name;
        this.stringRedisTemplate = stringRedisTemplate;
    }
    @Override
    public boolean tryLock(long timeoutSec) {
        //获取线程标示
        String threadId = ID_PREFIX + Thread.currentThread().getId();
        //释放锁
        Boolean success = stringRedisTemplate.opsForValue()
                .setIfAbsent(KEY_PREFIX + name, threadId, timeoutSec, TimeUnit.SECONDS);
        return Boolean.TRUE.equals(success);
    }
    @Override
    public void unlock() {
        //获取线程标示
        String threadId = ID_PREFIX + Thread.currentThread().getId();
        //获取锁中的标示
        String id = stringRedisTemplate.opsForValue().get(KEY_PREFIX + name);
        //判断标示是否一致
        if(threadId.equals(id)){
            //释放锁
            stringRedisTemplate.delete(KEY_PREFIX + name);
        }
    }
}

实战篇14

如果仅仅用id 两个不同的jvm就会出现两个相同的id 也有可能出现bug。因此,要将这块原子性,因为当线程一因为回收垃圾产生阻塞情况,而锁因为超时而释放时,线程二就有机会趁机而入,从而获取锁,从而发生多线程并发的情况

实战篇15

lua的使用手册

Lua 教程 | 菜鸟教程

实战篇16

thinking:没有lua标志?

 

解决方案:下载其中一个就好,不要两个都下载,两个插件效果是一样的。两个都下载会导致idea卡死的哦。

 

LUA:

--比较线程标示与锁中的标示是否一致
if(redis.call('get',KEYS[1])== ARGV[1])then
    --释放锁 del key
    return redis.call('del',KEYS[1])
end
return 0

JAVA:

package com.hmdp.utils;
import cn.hutool.core.lang.UUID;
import org.apache.ibatis.javassist.ClassPath;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;

import java.util.Collection;
import java.util.Collections;
import java.util.concurrent.TimeUnit;
public class SimpleRedisLock implements ILock {
    private String name;
    private StringRedisTemplate stringRedisTemplate;
    private static final String KEY_PREFIX = "lock:";
    private static final String ID_PREFIX = UUID.randomUUID().toString(true)+"-";
    //提前读好这个文件,避免多次读取
    private static final DefaultRedisScript<Long> UNLOCK_SCRIPT;
    //因为是静态的,因此在静态代码块里面搞
    static {
        UNLOCK_SCRIPT = new DefaultRedisScript<>();
        UNLOCK_SCRIPT.setLocation(new ClassPathResource("unlock.lua"));
        UNLOCK_SCRIPT.setResultType(Long.class);
    }

    public SimpleRedisLock(String name, StringRedisTemplate stringRedisTemplate) {
        this.name = name;
        this.stringRedisTemplate = stringRedisTemplate;
    }
    @Override
    public boolean tryLock(long timeoutSec) {
        //获取线程标示
        String threadId = ID_PREFIX + Thread.currentThread().getId();
        //释放锁
        Boolean success = stringRedisTemplate.opsForValue()
                .setIfAbsent(KEY_PREFIX + name, threadId, timeoutSec, TimeUnit.SECONDS);
        return Boolean.TRUE.equals(success);
    }
    @Override
    public void unlock() {
        //调用lua脚本
        stringRedisTemplate.execute(UNLOCK_SCRIPT, Collections.singletonList(KEY_PREFIX+name),ID_PREFIX + Thread.currentThread().getId());
    }
//    @Override
//    public void unlock() {
//        //获取线程标示
//        String threadId = ID_PREFIX + Thread.currentThread().getId();
//        //获取锁中的标示
//        String id = stringRedisTemplate.opsForValue().get(KEY_PREFIX + name);
//        //判断标示是否一致
//        if(threadId.equals(id)){
//            //释放锁
//            stringRedisTemplate.delete(KEY_PREFIX + name);
//        }
    }

 

 

实战篇17

 

这个网站不知道为什么打不开。。

实战篇18

 <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.13.6</version>
        </dependency>
package com.hmdp.config;

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RedissonConfig {
    @Bean
    public RedissonClient redissonClient(){
        //配置
        Config config = new Config();
        config.useSingleServer().setAddress("redis://localhost:6379");
        //创建RedissonClient对象
    return Redisson.create(config);
    }
}
package com.hmdp.service.impl;

import com.hmdp.dto.Result;
import com.hmdp.entity.SeckillVoucher;
import com.hmdp.entity.User;
import com.hmdp.entity.Voucher;
import com.hmdp.entity.VoucherOrder;
import com.hmdp.mapper.VoucherOrderMapper;
import com.hmdp.service.ISeckillVoucherService;
import com.hmdp.service.IVoucherOrderService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmdp.utils.RedisIdWorker;
import com.hmdp.utils.SimpleRedisLock;
import com.hmdp.utils.UserHolder;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.aop.framework.AopContext;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDateTime;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author 虎哥
 * @since 2021-12-22
 */
@Service
public class VoucherOrderServiceImpl extends ServiceImpl<VoucherOrderMapper, VoucherOrder> implements IVoucherOrderService {
    @Resource
    private ISeckillVoucherService seckillVoucherService;
    @Resource
    private RedisIdWorker redisIdWorker;
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Resource
    private RedissonClient redissonClient;
    @Override
    public Result seckillVoucher(Long voucherId) {
        //1,查询优惠券
        SeckillVoucher voucher = seckillVoucherService.getById(voucherId);
        //2,判断秒杀是否开始
        if (voucher.getBeginTime().isAfter(LocalDateTime.now())) {
            //尚未开始
            return Result.fail("秒杀尚未开始!");
        }
        //3,判断秒杀是否已经结束
        if (voucher.getBeginTime().isBefore(LocalDateTime.now())) {
            //已经结束
            return Result.fail("秒杀已经结束!");
        }
        //4,判断库存是否充足
        if (voucher.getStock() < 1) {
            //库存不足
            return Result.fail("库存不足!");
        }
        //一人一锁,提高效率
        //intern 保证指定的userId对应指定的锁
        //先获取锁,再完成以下方法,先完成方法,再释放锁,才能确保线程安全
        Long userId = UserHolder.getUser().getId();
        //synchronized(userId.toString().intern()){
        //获取代理对象(事务)
        //创建锁对象
       // SimpleRedisLock lock = new SimpleRedisLock("order:" + userId, stringRedisTemplate);
        RLock lock = redissonClient.getLock("lock:order:" + userId);
        //获取锁
        //不传参数,代表我失败了立即返回
        boolean isLock = lock.tryLock();
        //判断是否获取锁成功
        if(!isLock){
            //获取锁失败,返回错误或重试
            return Result.fail("不允许重复下单");
        }
        try {
            //获取代理对象(事务)
            IVoucherOrderService proxy = (IVoucherOrderService) AopContext.currentProxy();
            return proxy.createVoucherOrder(voucherId);
        } finally {
            //释放锁
            lock.unlock();
        }
        // }
    }
    //加上事务,因为这里有两张表
    @Transactional
    public  Result createVoucherOrder(Long voucherId) {
        //5,一人一单
        Long userId = UserHolder.getUser().getId();
        //5.1 查询订单
        int count = query().eq("user_id", userId).eq("voucher_id", voucherId).count();
        //5.2 判断是否存在
        if (count > 0) {
            // 用户已经购买过了
            return Result.fail("用户已经购买过一次!");
        }
        //6,扣减库存
        boolean success = seckillVoucherService
                .update().setSql("stock = stock - 1")
                .eq("voucher_id", voucherId)
                .gt("stock", 0)
                .update();
        if (!success) {
            //扣除失败
            return Result.fail("库存不足!");
        }
        //7,创建订单
        VoucherOrder voucherOrder = new VoucherOrder();
        //7.1 订单id
        long orderId = redisIdWorker.nextId("order");
        voucherOrder.setId(orderId);
        //7.2 用户id
        voucherOrder.setUserId(userId);
        //7.3 优惠券id
        voucherOrder.setVoucherId(voucherId);
        save(voucherOrder);
        //8,返回订单id
        return Result.ok(orderId);
        }
    }

实战篇20

实战篇21 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1274447.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【微服务 SpringCloudAlibaba】实用篇 · Feign服务远程调用

微服务&#xff08;7&#xff09; 文章目录 微服务&#xff08;7&#xff09;1. Feign替代RestTemplate1&#xff09;引入依赖2&#xff09;添加注解3&#xff09;编写Feign的客户端4&#xff09;测试5&#xff09;总结 2.自定义配置2.1 配置文件方式2.2 Java代码方式 3. Feign…

Unity随笔1 - 安卓打包JDK not found

今天遇到一个很奇怪的事情&#xff0c;之前可以正常打安卓包&#xff0c;但是突然报错如下&#xff1a; 提示很明显&#xff0c;找不到JDK了。可是我在下载Unity的时候明明安装了所有需要的组件&#xff0c;为什么今天突然不行。 看了眼Unity hub里面&#xff0c;没问题。 那就…

go elasticsearch 测试实例

// 查询列表数据 func QueryOperateList(ctx context.Context, esClient *elastic.Client, index string, pageNum, pageSize int, start, end int64, execSql string, list []interface{}, operateAccount string, operateAddr string, maxRows, minRows int, dbAddr, namespa…

【UGUI】实现背包的常用操作

1. 添加物品 首先&#xff0c;你需要一个包含物品信息的类&#xff0c;比如 InventoryItem&#xff1a; using UnityEngine;[CreateAssetMenu(fileName "NewInventoryItem", menuName "Inventory/Item")] public class InventoryItem : ScriptableObje…

【Azure 架构师学习笔记】- Azure Databricks (1) - 环境搭建

本文属于【Azure 架构师学习笔记】系列。 本文属于【Azure Databricks】系列。 前言 Databricks 已经成为了数据科学的必备工具&#xff0c;今时今日你已经很难抛开它来谈大数据&#xff0c;它常用于做复杂的ETL中的T&#xff0c; 数据分析&#xff0c;数据挖掘等&#xff0c;…

[架构之路-255]:目标系统 - 设计方法 - 软件工程 - 软件设计 - 架构设计 - 软件架构风格

目录 前言&#xff1a; 一、建筑风格 1.1 什么是建筑风格 1.2 常见的建筑风格 1.3 如何区分不同的建筑风格 二、软件架构风格概述 2.1 什么是软件架构风格 2.2 如何区分不同的软件架构风格 2.3 软件架构风格的发展阶段 2.4 软件架构风格与软件架构的区别 2.5 常见的…

在 S/4HANA、ECC 和 ERP 上轻松扩展或简化 SAP WM,并将其自动化到移动环境中

为您的 SAP WM 提供完整的本地 SAP 图形用户界面 基于原生通道架构&#xff08;NCA&#xff09;&#xff0c;iOS、Android 和手持 Scanguns 版 Liquid UI 可与 SAP WM 原生连接&#xff0c;同时保留 SAP GUI 丰富的事务处理功能。它使您无需编程即可直接从移动设备访问 MIGO、…

知识图谱最简单的demo实现——基于pyvis

1、前言 我们在上篇文章中介绍了知识图谱的简单实现&#xff0c;最后使用neo4j进行了展示&#xff0c;对于有些情况我们可能并不想为了查看知识图的结果再去安装一个软件去实现&#xff0c;那么我们能不能直接将三元组画出来呢/ 接下来我们就介绍一个可视化的工具pyvis&#…

Memcached最新2023年面试题,高级面试题及附答案解析

文章目录 01、Memcached是什么&#xff0c;有什么作用&#xff1f;02、Memcached的多线程是什么&#xff1f;如何使用它们&#xff1f;03、Memcached与Redis的区别&#xff1f;04、如果缓存数据在导出导入之间过期了&#xff0c;怎么处理这些数据呢&#xff1f;05、如何实现集群…

网站提示不安全?

随着互联网的普及和发展&#xff0c;网络安全问题日益严重。黑客攻击、数据泄露、恶意软件等问题层出不穷&#xff0c;给企业和个人带来了巨大的损失。在这个背景下&#xff0c;确保网站安全显得尤为重要&#xff0c;而使用SSL证书是解决这些问题的有效措施。 什么是SSL证书&am…

【23-24 秋学期】NNDL 作业9 RNN - SRN

简单循环网络&#xff08;Simple Recurrent Network&#xff0c;SRN&#xff09;只有一个隐藏层的神经网络&#xff0e; 目录 1. 实现SRN &#xff08;1&#xff09;使用Numpy &#xff08;2&#xff09;在1的基础上&#xff0c;增加激活函数tanh &#xff08;3&#xff0…

剪辑素材,6个可白嫖的视频素材网站

找视频素材就上这6个网站&#xff0c;免费下载&#xff0c;赶紧收藏好&#xff01; 1、菜鸟图库 https://www.sucai999.com/video.html?vNTYxMjky 菜鸟图库网素材非常丰富&#xff0c;网站主要以设计类素材为主&#xff0c;高清视频素材也很多&#xff0c;像风景、植物、动物…

HTML—列表、表格、表单

1、列表 作用&#xff1a;布局内容排列整齐的区域 列表分类&#xff1a;无序列表、有序列表、定义列表 1.1 无序列表 作用&#xff1a;布局排列整齐的不需要规定顺序的区域 标签&#xff1a;ul 嵌套 li&#xff0c;ul 是无序列表&#xff0c;li 是列表条目 注意事项&#…

服务器数据恢复—EMC存储raid5故障导致上层应用崩溃的数据恢复案例

服务器存储数据恢复环境&#xff1a; EMC某型号存储&#xff0c;8块组建一组raid5磁盘阵列。上层操作系统采用zfs文件系统。 服务器存储故障&分析&#xff1a; raid5阵列中有2块硬盘未知原因离线&#xff0c;raid5阵列崩溃&#xff0c;上层应用无法正常使用。 服务器数据恢…

luceda ipkiss教程 38:等长波导布线

这次介绍通过调整圆弧角度实现等长弯曲波导布线的案例&#xff1a; 四段波导的长度分别为&#xff1a; 所有代码如下&#xff1a; from si_fab import all as pdk from ipkiss3 import all as i3 class MMI1x4(i3.PCell):_name_prefix "MMI1x4" # adding a nam…

事务--02---TCC模式

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 TCC模式两阶段提交 的模型 1.流程分析阶段一&#xff08; Try &#xff09;&#xff1a;阶段二&#xff08;Confirm)&#xff1a;阶段二(Canncel)&#xff1a; 2.事…

利用ARCGIS做地下水脆弱性评价分析

&#xff08;一&#xff09;行政边界数据、土地利用数据和土壤类型数据 本文所用到的河北唐山行政边界数据、土地利用数据和土壤类型数据均来源于中国科学院资源环境科学与数据中心&#xff08;https://www.resdc.cn/Default.aspx&#xff09;。 &#xff08;二&#xff09;地…

【shell】正则表达式和AWK

一.正则表达式 通配符匹配文件&#xff08;而且是已存在的文件&#xff09; 基本正则表达式扩展正则表达式 可以使用 man 手册帮助 正则表达式&#xff1a;匹配的是文章中的字符 通配符&#xff1a;匹配的是文件名 任意单个字符 1.元字符&#xff08;字符匹配&…

SAP_ABAP_编程基础_内表_创建内表 / 填充内表 / 读取内表 /修改和删除内表行 / 内表排序 / 创建顺序表 / 比较内表 / 初始化内表

SAP ABAP 顾问&#xff08;开发工程师&#xff09;能力模型_Terry谈企业数字化的博客-CSDN博客文章浏览阅读470次。目标&#xff1a;基于对SAP abap 顾问能力模型的梳理&#xff0c;给一年左右经验的abaper 快速成长为三年经验提供超级燃料&#xff01;https://blog.csdn.net/j…

Pandas进阶:transform 数据转换的常用技巧

引言 本次给大家介绍一个功能超强的数据处理函数transform&#xff0c;相信很多朋友也用过&#xff0c;这里再次进行详细分享下。 transform有4个比较常用的功能&#xff0c;总结如下&#xff1a; 转换数值 合并分组结果 过滤数据 结合分组处理缺失值 一. 转换数值 pd.…