redis的分布式锁核心在 setnx 方法,成功返回1,失败返回0
案例:搭建springboot集成redis实现分布式锁,100个线程同时生产订单号场景
思路:
1,获取锁,成功执行第2步,失败就等待获取锁成功
2,执行业务
3,释放锁
1,pom文件
<!--集成SpringBoot-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.6.2</version>
</dependency>
<!--集成redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.6.3</version>
</dependency>
2.创建 RedisUtil 类
package com.hyw.util;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import java.time.Duration;
import java.util.concurrent.TimeUnit;
@Component
public class RedisUtil {
/**
* 获取redis模板
*/
@Autowired
private StringRedisTemplate redis;
public void setStr(String key, String value){
setStr(key, value, null);
}
/**
* 存字符串
* @param key
* @param value
* @param timeOut
*/
public void setStr(String key, String value, Long timeOut){
redis.opsForValue().set(key, value);
if(timeOut != null)
redis.expire(key, timeOut, TimeUnit.SECONDS);
}
/**
* SetNx命令 每次SetNx检查该 key是否已经存在,如果已经存在的话不会执行任何操作。返回为0 如果已经不存在的话直接新增该key
* @param key
* @param value
* @param timeOut
* @return
*/
public Integer setnx(String key, String value, Long timeOut){
boolean flag = redis.opsForValue().setIfAbsent(key,value, timeOut, TimeUnit.SECONDS);
return flag?1:0;
}
public Integer setnx(String key, String value){
return setnx(key, value, null);
}
/**
* 删除key
* @param key
*/
public void del(String key){
redis.delete(key);
}
public String getStr(String key){
return redis.opsForValue().get(key);
}
}
3.创建 RedisLock类
package com.hyw.lock;
import com.hyw.contans.OrderContans;
import com.hyw.util.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.UUID;
@Component
public class RedisLock {
@Autowired
private RedisUtil redisUtil;
public String getLock(String lockKey, int notLockTime){
//计算尝试获取锁超时时间
Long endTime = System.currentTimeMillis() + notLockTime;
//超时就退出循环
while (System.currentTimeMillis() < endTime){
String lockValue = UUID.randomUUID().toString();
if(redisUtil.setnx(lockKey, lockValue, notLockTime*1l/1000) == OrderContans.lockSuccess){
System.out.println(Thread.currentThread().getName() +"获取锁成功!");
return lockValue;
}
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return null;
}
/**
* 释放锁
* @param lockKey
* @param lockValue
* @return
*/
public void unlock(String lockKey, String lockValue){
//判断lockValue 是否抢到锁的用户,是就删除,避免把别人的锁给删了
if(lockValue.equals(redisUtil.getStr(lockKey))){
redisUtil.del(lockKey);
System.out.println(Thread.currentThread().getName() +"释放锁成功!");
}
}
}
4,创建常量类OrderContans
package com.hyw.contans;
public class OrderContans {
/**
* 1 获取到锁, 0 没获取到锁
*/
public static int lockSuccess = 1;
/**
* 订单锁 key
*/
public static final String LOCK_KEY = "order_lock";
/**
* 设置锁超时 10秒
*/
public static int notLockTime = 10000;
}
5,service成创建OrderService类,实现生成订单业务
package com.hyw.service;
import com.hyw.contans.OrderContans;
import com.hyw.lock.RedisLock;
import com.hyw.util.OrderUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
@Component
public class OrderService {
@Autowired
private RedisLock redisLock;
public void getOrderNum(){
//1.获取锁
String lockValue = redisLock.getLock(OrderContans.LOCK_KEY, OrderContans.notLockTime);
if(StringUtils.isEmpty(lockValue)){
System.out.println("获取锁失败!");
return;
}
System.out.println("获取订单号成功..."+ OrderUtil.getOrderNumber());
//3.释放锁
redisLock.unlock(OrderContans.LOCK_KEY, lockValue);
System.out.println();
}
}
6,创建控制器层OrderController,测试结果
package com.hyw.controller;
import com.hyw.service.OrderService;
import com.hyw.util.RedisUtil;
@RestController
public class OrderController {
@Autowired
private RedisUtil redisUtil;
@Autowired
private OrderService orderService;
/**
* 获取订单号
* @return
*/
@RequestMapping("/getOrderNum")
public String getOrderNum(){
for (int i = 0; i < 100; i++) {
new Thread(new Runnable() {
public void run() {
orderService.getOrderNum();
}
}).start();
}
return "success";
}
}
7,springboot启动类
package com.hyw;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import java.text.SimpleDateFormat;
import java.util.Date;
@SpringBootApplication
public class ApplicationMain {
public static void main(String[] args) {
SpringApplication.run(ApplicationMain.class, args);
}
}
运行结果:
http://localhost:8888/getOrderNum