最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot集成Redisson實現(xiàn)分布式鎖的示例代碼

 更新時間:2026年04月08日 08:33:44   作者:Zzxy  
本文介紹了使用Redisson實現(xiàn)分布式鎖以解決并發(fā)預約號源超賣問題,首先添加Redisson依賴并配置RedissonClient,接著在預約服務中引入分布式鎖,并自定義扣減號源方法,最后總結了分布式鎖的實現(xiàn)原理,包括鎖粒度、鎖超時、可重入性和WatchDog機制,需要的朋友可以參考下

1、集成 Redisson

Redisson 是 Redis 官方推薦的 Java 客戶端,實現(xiàn)了 可靠的分布式鎖、可重入鎖、讀寫鎖 等,封裝了底層復雜性。

1.1 添加 Redisson 依賴

<!-- Redisson(Redis分布式鎖客戶端) -->
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>3.23.4</version>
</dependency>

1.2 配置 RedissonClient

在application.yml中添加:

spring:
  redis:
    host: localhost
    port: 6379
    # Redisson自動配置,無需額外配置

2、分布式鎖解決并發(fā)預約號源超賣問題

2.1 預約服務中,增加分布式鎖

修改AppointmentServiceImpl.bookAppointment方法:

package com.example.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.springBoot.hospital.entity.Appointment;
import com.springBoot.hospital.entity.Department;
import com.springBoot.hospital.mapper.AppointmentMapper;
import com.springBoot.hospital.mapper.DepartmentMapper;
import com.springBoot.hospital.service.AppointmentService;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.text.SimpleDateFormat;
import java.util.concurrent.TimeUnit;
@Service
public class AppointmentServiceImpl implements AppointmentService {
    @Autowired
    private DepartmentMapper departmentMapper;
    @Autowired
    private AppointmentMapper appointmentMapper;
    @Autowired
    private RedissonClient redissonClient;
/*
    //預約操作后清除該用戶的預約緩存
    @CacheEvict(value = "appointments", key = "'user:' + #userId + '_page:*'",allEntries = true)
    //Spring Cache 原生不支持通配符 * 在 key 中的模糊匹配。
    //allEntries = true會清空整個緩存名下的所有條目
    @Override
    @Transactional(rollbackFor = Exception.class) //任何異常都回滾
    public boolean bookAppointment(String appointmentNo,Long userId, Long departmentId, String appointmentDate) {
        //1\插入預約記錄
        Appointment appointment = new Appointment();
        appointment.setAppointmentNo(appointmentNo);
        appointment.setUserId(userId);
        appointment.setDepartmentId(departmentId);
        //將字符串轉為Date(格式:yyyy-mm-dd)
        try {
            //使用SimpleDateFormat 定義日期格式
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-mm-dd");
            Date date = sdf.parse(appointmentDate); //字符串轉為Date
            appointment.setAppointmentDate(date);
        } catch (Exception e) {
            throw new RuntimeException("日期格式錯誤",e);
        }
        appointment.setStatus("PENDING"); //狀態(tài):待就診
        int rows = appointmentMapper.insert(appointment);
        if(rows == 0){
            throw new RuntimeException("插入預約失敗");
        }
        //2\扣減號源,department中
        //使用條件更新,避免并發(fā)
        LambdaUpdateWrapper<Department> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(Department::getId,departmentId)
                .gt(Department::getRemaining,0)
                .setSql("order_num = order_num - 1");
        int updateRows = departmentMapper.update(null,updateWrapper);
        if(updateRows == 0){
            throw new RuntimeException("號源不足或科室不存在");
        }
        return true;
    }
 */
    @Override
    @Transactional(rollbackFor = Exception.class) //事務管理,任何異常都回滾
    public boolean bookAppointment(String appointmentNo, Long userId, Long departmentId, String appointmentDate) {
        //構建分布式鎖的key(科室Id + 預約日期)
        String lockKey = "lock:dept:" + departmentId + ":date:" + appointmentDate;
        //創(chuàng)建對象,Redisson 根據 key 獲取一個可重入鎖對象。
        RLock lock = redissonClient.getLock(lockKey);
        boolean locked = false;
        try {
            // 嘗試加鎖,最多等待3秒,上鎖后10秒自動釋放(防止死鎖)
            locked = lock.tryLock(3, 10, TimeUnit.SECONDS);
            if(!locked){
                throw new RuntimeException("系統(tǒng)繁忙,請稍后重試");
            }
            //1 檢查號源
            Department dept = departmentMapper.selectById(departmentId);
            if(dept == null || dept.getRemaining() == null || dept.getRemaining() <= 0){
                throw new RuntimeException("號源不足");
            }
            //2 插入預約記錄
            Appointment appointment = new Appointment();
            appointment.setAppointmentNo(appointmentNo);
            appointment.setUserId(userId);
            appointment.setDepartmentId(departmentId);
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            appointment.setAppointmentDate(sdf.parse(appointmentDate));
            appointment.setStatus("PENDING");
            int insertRows = appointmentMapper.insert(appointment);
            if(insertRows == 0){
                throw new RuntimeException("插入預約失敗");
            }
            //3 扣減號源
            //使用自定義扣減號源方法,SQL條件更新號源數(shù)量
            int updateRows = departmentMapper.decreaseRemaining(departmentId);
            if(updateRows == 0){
                throw new RuntimeException("扣減號源失敗,可能已被搶走");
            }
            return true;
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage(),e);
        } finally {
            //釋放鎖,只釋放當前線程持有的鎖
            if(locked && lock.isHeldByCurrentThread()){
                lock.unlock();
            }
        }
    }
    //分頁查詢緩存,分頁參數(shù)影響key
    @Cacheable(value = "appointments", key = "'user:' + #userId + '_page:' + #pageNum + '_size:' + #pageSize")
    @Override
    public IPage<Appointment> findAppointmentsByUserId(Long userId, Integer pageNum, Integer pageSize) {
        Page<Appointment> page = new Page<>(pageNum,pageSize);
        LambdaQueryWrapper<Appointment> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Appointment::getUserId,userId)
                .orderByDesc(Appointment::getAppointmentDate);
        return appointmentMapper.selectPage(page,queryWrapper);
    }
}

2.2 自定義扣減號源方法

在DepartmentMapper中添加自定義扣減方法

// DepartmentMapper.java
@Mapper
public interface DepartmentMapper extends BaseMapper<Department> {
    
    // 自定義扣減號源(使用樂觀鎖防止超賣)
    @Update("UPDATE department SET order_num = order_num - 1 where id = #{departmentId}")
    int decreaseRemaining(@Param("departmentId") Long departmentId);
}

3、分布式鎖原理總結

  • 鎖粒度:應該細化到 科室+日期,而不是整個系統(tǒng)
  • 鎖超時:設置合理超時時間,避免業(yè)務執(zhí)行過長導致鎖自動釋放
  • 可重入性:Redisson支持可重入,同一線程可重復獲取
  • Watch Dog機制:Redisson的鎖會自動續(xù)期(默認30秒),防止業(yè)務未完成鎖過期

以上就是SpringBoot集成Redisson實現(xiàn)分布式鎖的示例代碼的詳細內容,更多關于SpringBoot Redisson分布式鎖的資料請關注腳本之家其它相關文章!

相關文章

  • Java程序的初始化順序,static{}靜態(tài)代碼塊和實例語句塊的使用方式

    Java程序的初始化順序,static{}靜態(tài)代碼塊和實例語句塊的使用方式

    這篇文章主要介紹了Java程序的初始化順序,static{}靜態(tài)代碼塊和實例語句塊的使用方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-01-01
  • 在Spring?Boot使用Undertow服務的方法

    在Spring?Boot使用Undertow服務的方法

    Undertow是RedHAT紅帽公司開源的產品,采用JAVA開發(fā),是一款靈活,高性能的web服務器,提供了NIO的阻塞/非阻塞API,也是Wildfly的默認Web容器,這篇文章給大家介紹了在Spring?Boot使用Undertow服務的方法,感興趣的朋友跟隨小編一起看看吧
    2023-05-05
  • 通過Java實現(xiàn)zip文件與rar文件解壓縮的詳細步驟

    通過Java實現(xiàn)zip文件與rar文件解壓縮的詳細步驟

    這篇文章主要給大家介紹了如何通過?Java?來完成?zip?文件與?rar?文件的解壓縮,文中通過代碼示例講解的非常詳細,對大家的學習或工作有一定的幫助,需要的朋友可以參考下
    2024-07-07
  • springboot 攔截器執(zhí)行兩次的解決方案

    springboot 攔截器執(zhí)行兩次的解決方案

    這篇文章主要介紹了springboot 攔截器執(zhí)行兩次的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • Maven添加reactor依賴失敗的解決方案

    Maven添加reactor依賴失敗的解決方案

    起初是自己在學spring boot3,結果到了reactor這一部分的時候,在項目的pom.xml文件中添加下列依賴報錯,接下來通過本文給大家介紹Maven添加reactor依賴失敗的解決方案,需要的朋友可以參考下
    2024-06-06
  • 解決SpringMVC接收不到ajaxPOST參數(shù)的問題

    解決SpringMVC接收不到ajaxPOST參數(shù)的問題

    今天小編就為大家分享一篇解決SpringMVC接收不到ajaxPOST參數(shù)的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-08-08
  • logback日志級別設置無效問題及解決

    logback日志級別設置無效問題及解決

    文章總結:文章介紹了在Spring?Boot項目中配置日志級別時,優(yōu)先級問題,通過查閱資料發(fā)現(xiàn),配置文件中的logging.level.xx也能配置日志級別,且優(yōu)先級比logback.xml高,文章還提供了源碼分析,說明了Spring?Boot如何處理日志級別配置
    2025-11-11
  • Maven中optional和scope元素的使用弄明白了嗎

    Maven中optional和scope元素的使用弄明白了嗎

    這篇文章主要介紹了Maven中optional和scope元素的使用弄明白了嗎,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-12-12
  • 關于feign接口動態(tài)代理源碼解析

    關于feign接口動態(tài)代理源碼解析

    這篇文章主要介紹了關于feign接口動態(tài)代理源碼解析,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • java微信公眾號發(fā)送消息模板

    java微信公眾號發(fā)送消息模板

    這篇文章主要為大家詳細介紹了java微信公眾號發(fā)送消息模板,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-08-08

最新評論

故城县| 栖霞市| 孟津县| 铜山县| 罗山县| 岑巩县| 徐州市| 太湖县| 奉节县| 务川| 湖北省| 贡嘎县| 青阳县| 加查县| 永胜县| 田东县| 高淳县| 东源县| 呼伦贝尔市| 布尔津县| 游戏| 博兴县| 车险| 鄂托克旗| 芜湖县| 闽清县| 渭源县| 苏尼特左旗| 博罗县| 上栗县| 丹棱县| 东城区| 铅山县| 共和县| 宁陵县| 弥渡县| 奉贤区| 寻甸| 安乡县| 达拉特旗| 崇阳县|