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

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

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

1、集成 Redisson

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

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自動(dòng)配置,無需額外配置

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

2.1 預(yù)約服務(wù)中,增加分布式鎖

修改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;
/*
    //預(yù)約操作后清除該用戶的預(yù)約緩存
    @CacheEvict(value = "appointments", key = "'user:' + #userId + '_page:*'",allEntries = true)
    //Spring Cache 原生不支持通配符 * 在 key 中的模糊匹配。
    //allEntries = true會(huì)清空整個(gè)緩存名下的所有條目
    @Override
    @Transactional(rollbackFor = Exception.class) //任何異常都回滾
    public boolean bookAppointment(String appointmentNo,Long userId, Long departmentId, String appointmentDate) {
        //1\插入預(yù)約記錄
        Appointment appointment = new Appointment();
        appointment.setAppointmentNo(appointmentNo);
        appointment.setUserId(userId);
        appointment.setDepartmentId(departmentId);
        //將字符串轉(zhuǎn)為Date(格式:yyyy-mm-dd)
        try {
            //使用SimpleDateFormat 定義日期格式
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-mm-dd");
            Date date = sdf.parse(appointmentDate); //字符串轉(zhuǎn)為Date
            appointment.setAppointmentDate(date);
        } catch (Exception e) {
            throw new RuntimeException("日期格式錯(cuò)誤",e);
        }
        appointment.setStatus("PENDING"); //狀態(tài):待就診
        int rows = appointmentMapper.insert(appointment);
        if(rows == 0){
            throw new RuntimeException("插入預(yù)約失敗");
        }
        //2\扣減號(hào)源,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("號(hào)源不足或科室不存在");
        }
        return true;
    }
 */
    @Override
    @Transactional(rollbackFor = Exception.class) //事務(wù)管理,任何異常都回滾
    public boolean bookAppointment(String appointmentNo, Long userId, Long departmentId, String appointmentDate) {
        //構(gòu)建分布式鎖的key(科室Id + 預(yù)約日期)
        String lockKey = "lock:dept:" + departmentId + ":date:" + appointmentDate;
        //創(chuàng)建對(duì)象,Redisson 根據(jù) key 獲取一個(gè)可重入鎖對(duì)象。
        RLock lock = redissonClient.getLock(lockKey);
        boolean locked = false;
        try {
            // 嘗試加鎖,最多等待3秒,上鎖后10秒自動(dòng)釋放(防止死鎖)
            locked = lock.tryLock(3, 10, TimeUnit.SECONDS);
            if(!locked){
                throw new RuntimeException("系統(tǒng)繁忙,請(qǐng)稍后重試");
            }
            //1 檢查號(hào)源
            Department dept = departmentMapper.selectById(departmentId);
            if(dept == null || dept.getRemaining() == null || dept.getRemaining() <= 0){
                throw new RuntimeException("號(hào)源不足");
            }
            //2 插入預(yù)約記錄
            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("插入預(yù)約失敗");
            }
            //3 扣減號(hào)源
            //使用自定義扣減號(hào)源方法,SQL條件更新號(hào)源數(shù)量
            int updateRows = departmentMapper.decreaseRemaining(departmentId);
            if(updateRows == 0){
                throw new RuntimeException("扣減號(hào)源失敗,可能已被搶走");
            }
            return true;
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage(),e);
        } finally {
            //釋放鎖,只釋放當(dāng)前線程持有的鎖
            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 自定義扣減號(hào)源方法

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

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

3、分布式鎖原理總結(jié)

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

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

相關(guān)文章

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

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

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

    在Spring?Boot使用Undertow服務(wù)的方法

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

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

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

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

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

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

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

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

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

    logback日志級(jí)別設(shè)置無效問題及解決

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

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

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

    關(guān)于feign接口動(dòng)態(tài)代理源碼解析

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

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

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

最新評(píng)論

福泉市| 宁南县| 吉木萨尔县| 晋中市| 巴青县| 仪陇县| 若尔盖县| 赤水市| 乃东县| 临夏市| 武川县| 忻州市| 陵水| SHOW| 井陉县| 定西市| 墨脱县| 正阳县| 东乌珠穆沁旗| 句容市| 图片| 武鸣县| 确山县| 肥乡县| 湘潭市| 高邑县| 肇庆市| 台东县| 山丹县| 桂阳县| 夏河县| 杭锦旗| 封丘县| 金溪县| 仪陇县| 东乌| 无锡市| 西畴县| 黄山市| 图木舒克市| 龙川县|