IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 使用Redis设置分布式锁 -> 正文阅读

[Java知识库]使用Redis设置分布式锁

使用Redis设置分布式锁

目录结构

在这里插入图片描述

首先引入依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.zhou</groupId>
    <artifactId>springboot-redis</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboot-redis</name>
    <description>Demo project for Spring Boot</description>


    <parent>
        <artifactId>springboot-vue</artifactId>
        <groupId>com.zhou</groupId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>

    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.2.0</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>



</project>

配置redis和mysql

# 应用名称
spring.application.name=springboot-redis
# 应用服务 WEB 访问端口
server.port=8080


# Redis地址
spring.redis.host=127.0.0.1
# Redis端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
# spring.redis.password=123456
# Redis数据库索引(默认为0)
spring.redis.database=0
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.jedis.pool.max-active=10
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.jedis.pool.max-wait=-1ms
# 连接池中的最大空闲连接
spring.redis.jedis.pool.max-idle=8
# 连接池中的最小空闲连接
spring.redis.jedis.pool.min-idle=0
# 连接超时时间(毫秒)
spring.redis.timeout=10000ms

spring.datasource.url= jdbc:mysql://192.168.1.4:3306/springboot_redis?useUnicode=true&characterEncoding=UTF-8&useSSL=false&autoReconnect=true&failOverReadOnly=false&serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

logging.level.com.zhou=debug



设置单机版的锁

首先创建一个实体类,这个实体类用来返回数据库数据

package com.zhou.springbootredis.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

/**
 * @author DELL
 * @version 1.0
 * @Description
 * @date 2022/4/21 17:06
 */
@Data
@TableName("user")
@NoArgsConstructor
@AllArgsConstructor
public class UserEntity {
    /**
     * 主键
     */
    @TableId(type = IdType.ID_WORKER)
    private Long id;

    /**
     * 姓名
     */
    private String name;

    /**
     * 年龄
     */
    private String passward;
}

创建一个mapper

package com.zhou.springbootredis.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.zhou.springbootredis.entity.UserEntity;

/**
 * @author DELL
 * @version 1.0
 * @Description
 * @date 2022/4/21 17:35
 */

public interface UserMapper extends BaseMapper<UserEntity> {
}

创建一个接口

package com.zhou.springbootredis.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.zhou.springbootredis.entity.UserEntity;

/**
 * @author DELL
 * @version 1.0
 * @Description
 * @date 2022/4/21 17:36
 */
public interface UserService extends IService<UserEntity> {
    Object getUserById(Integer id);
    void parallel(Integer id);
}

实现接口,getUserById方法实现的单机版锁,parallel直接模拟并发

package com.zhou.springbootredis.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zhou.springbootredis.entity.UserEntity;
import com.zhou.springbootredis.mapper.UserMapper;
import com.zhou.springbootredis.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * @author DELL
 * @version 1.0
 * @Description
 * @date 2022/4/21 17:38
 */
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, UserEntity> implements UserService {

    private final String REDISLOCK="REDISLOCK";

    @Autowired
    public RedisTemplate redisTemplate;

    @Resource
    private UserMapper userMapper;

    /**
     * 单机版的一个锁
     * @param id
     * @return
     */
    @Override
    public  Object getUserById(Integer id) {
        String key = "user:" + id;
        Object userObj = redisTemplate.opsForValue().get(key);
        if(userObj == null){
            synchronized (this.getClass()){
                userObj = redisTemplate.opsForValue().get(key);
                if(userObj == null ){
                    log.debug("查询数据库");
                    // 查数据库
                    UserEntity userEntity = userMapper.selectById(id);
                    redisTemplate.opsForValue().set(key,userEntity);
                    return userEntity;
                }else{
                    log.debug("查询缓存,进入锁");
                    return userObj;
                }
            }

        }else{
            log.debug("查询缓存");
        }
        return userObj;
    }

    @Override
    public void parallel(Integer id){
        //10线程处理100个并发,自己随便设
        ExecutorService es = Executors.newFixedThreadPool(10);
        for(int i=0 ;i<100;i++){
            es.submit(new Runnable() {
                @Override
                public void run() {
                    getUserById(id);
                }
            });
        }
        try {
            es.shutdown();
            if(!es.awaitTermination(1,TimeUnit.MINUTES)){
                es.shutdownNow();
            }
            log.debug("所有任务完成");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }




}

创建controller

package com.zhou.springbootredis.controller;

import com.zhou.springbootredis.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author DELL
 * @version 1.0
 * @Description
 * @date 2022/4/21 17:43
 */
@RestController
@RequestMapping("redis")
public class Redis2Controller {

    @Autowired
    UserService userService;

    @GetMapping("getUserById/{id}")
    public Object getUserById(@PathVariable("id") Integer id){
        return userService.getUserById(id);
    }

    @GetMapping("parallel/{id}")
    public Object parallel(@PathVariable("id") Integer id){

        userService.parallel(id);
        return "成功";
    }

}

在这里插入图片描述

结果只查了一次数据库

在这里插入图片描述

分布式锁

在UserService 接口文件中加如下两个接口

    Long redisLock(String userId,String selectKey);
    void parallelRedisLock(String selectKey);

在UserServiceImpl 文件中实现上面两个接口,redisLock是分布式锁的实现,parallelRedisLock直接模拟并发


    @Override
    public void parallelRedisLock(String selectKey){
        //100线程处理1000个并发,自己随便设
        ExecutorService es = Executors.newFixedThreadPool(10);
        for(int i=0 ;i<100;i++){
            int finalI = i;
            es.submit(new Runnable() {
                @Override
                public void run() {
                    redisLock("userId:"+ finalI,selectKey);
                }
            });
        }
        try {
            es.shutdown();
            //超时也干掉,这里是测试所以这么干
            if(!es.awaitTermination(1,TimeUnit.MINUTES)){
                es.shutdownNow();
            }
            log.debug("所有任务完成");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 利用redis设置一个分布式锁
     */
    @Override
    public Long redisLock(String userId,String selectKey){
        Long value;
        while (true) {
            //1、设置一把锁,这把锁的value是userId,后面只能是这个userId的删锁,设置一个10秒的过期时间,避免这个线程挂掉后没有删锁
            Boolean lock = redisTemplate.opsForValue().setIfAbsent(REDISLOCK, userId, 10, TimeUnit.SECONDS);
            if (lock) {
                log.debug(userId+"拿到锁");
                /* 先查key是否存在,不存在跳出
                Object value = redisTemplate.opsForValue().get(selectKey);
                if (StringUtils.isEmpty(value)) {
                    break;
                }*/
                //2、这个里面可以放自己的业务,这一句是给key的值加一
                value=redisTemplate.boundValueOps(selectKey).increment(1);
                //3、获取自己设置的锁,避免自己的锁过期了,再去把其它线程设置的锁删掉
                Object lockUserId = redisTemplate.opsForValue().get(REDISLOCK);
                //只删自己的锁
                if (userId.equals(lockUserId)) {
                    redisTemplate.delete(REDISLOCK);
                }
                break;
            }
            try {
                //log.debug(userId+"在休息");
                //没拿到锁就等一会
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return value;
    }

首先在redis中加入一个键值对,后面直接操作这个键值对

在这里插入图片描述

在controller中加入两个方法


    @GetMapping("redisLock")
    public Object redisLock(@RequestParam String userId,@RequestParam String key){
        return userService.redisLock(userId,key);
    }

    @GetMapping("parallelRedisLock")
    public Object parallelRedisLock(@RequestParam String key){
        userService.parallelRedisLock(key);
        return "成功";
    }

访问

在这里插入图片描述

结果

在这里插入图片描述
gitee地址

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-04-27 11:11:09  更:2022-04-27 11:13:44 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 2:55:44-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码