package lock; //加锁设置的参数 public class LockParam { //锁的key private String lockKey; //尝试获得锁的时间(单位:毫秒),默认值:3000毫秒 private Long tryLockTime; //尝试获得锁后,持有锁的时间(单位:毫秒),默认值:5000毫秒 private Long holdLockTime; public String getLockKey() { return lockKey; } public void setLockKey(String lockKey) { this.lockKey = lockKey; } public Long getTryLockTime() { return tryLockTime; } public void setTryLockTime(Long tryLockTime) { this.tryLockTime = tryLockTime; } public Long getHoldLockTime() { return holdLockTime; } public void setHoldLockTime(Long holdLockTime) { this.holdLockTime = holdLockTime; } public LockParam(String lockKey){ this(lockKey,1000*3L,1000*5L); }; public LockParam(String lockKey,Long tryLockTime){ this(lockKey,tryLockTime,1000*5L); }; public LockParam(String lockKey,Long tryLockTime,Long holdLockTime){ this.lockKey = lockKey; this.tryLockTime = tryLockTime; this.holdLockTime = holdLockTime; }; }
//redis分布式锁实现
package lock; import lombok.extern.slf4j.Slf4j; import redis.clients.jedis.Jedis; import java.util.Collections; import java.util.UUID; /** * redis分布式锁 */ @Slf4j public class RedisLock { //锁key的前缀 private final static String prefix_key = "redisLock:"; //释放锁的lua脚本 private final static String unLockScript = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"; //执行unLockScript脚本,释放锁成功值 private final static Long unLockSuccess = 1L; //加锁设置的参数(key值、超时时间、持有锁的时间) private LockParam lockParam; //尝试获得锁的截止时间【lockParam.getTryLockTime()+System.currentTimeMillis()】 private Long tryLockEndTime; //redis加锁的key private String redisLockKey; //redis加锁的vlaus private String redisLockValue; //redis加锁的成功标示 private Boolean holdLockSuccess= Boolean.FALSE; //jedis实例 private Jedis jedis; //获取jedis实例 private Jedis getJedis(){ return this.jedis; } //关闭jedis private void closeJedis(Jedis jedis){ jedis.close(); jedis = null; } public RedisLock(LockParam lockParam){ if(lockParam==null){ new RuntimeException("lockParam is null"); } if(lockParam.getLockKey()==null || lockParam.getLockKey().trim().length()==0){ new RuntimeException("lockParam lockKey is error"); } this.lockParam = lockParam; this.tryLockEndTime = lockParam.getTryLockTime()+System.currentTimeMillis(); this.redisLockKey = prefix_key.concat(lockParam.getLockKey()); this.redisLockValue = UUID.randomUUID().toString().replaceAll("-",""); //todo 到时候可以更换获取Jedis实例的实现 jedis = new Jedis("127.0.0.1",6379); } /** * 加锁 * @return 成功返回true,失败返回false */ public boolean lock() { while(true){ //判断是否超过了,尝试获取锁的时间 if(System.currentTimeMillis()>tryLockEndTime){ return false; } //尝试获取锁 holdLockSuccess = tryLock(); if(Boolean.TRUE.equals(holdLockSuccess)){ return true;//获取锁成功 } try { //获得锁失败,休眠50毫秒再去尝试获得锁,避免一直请求redis,导致redis cpu飙升 Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } } } /** * 执行一次加锁操作:成功返回true 失败返回false * @return 成功返回true,失败返回false */ private boolean tryLock() { try { String result = getJedis().set(redisLockKey,redisLockValue, "NX", "PX", lockParam.getHoldLockTime()); if ("OK".equals(result)) { return true; } }catch (Exception e){ e.printStackTrace(); //log.warn("tryLock failure redisLockKey:{} redisLockValue:{} lockParam:{}",redisLockKey,redisLockValue,lockParam,e); } return false; } /** * 解锁 * @return 成功返回true,失败返回false */ public Boolean unlock() { Object result = null; try { //获得锁成功,才执行lua脚本 if(Boolean.TRUE.equals(holdLockSuccess)){ //执行Lua脚本 result = getJedis().eval(unLockScript, Collections.singletonList(redisLockKey), Collections.singletonList(redisLockValue)); if (unLockSuccess.equals(result)) {//释放成功 return true; } } } catch (Exception e) { e.printStackTrace(); //log.warn("unlock failure redisLockKey:{} redisLockValue:{} lockParam:{} result:{}",redisLockKey,redisLockValue,lockParam,result,e); } finally { this.closeJedis(jedis); } return false; } }
//测试类
package lock; import lombok.extern.slf4j.Slf4j; @Slf4j public class test { static String lockKey = "66688"; public static void main(String[] args) throws InterruptedException { System.out.println(); System.out.println("下面测试两个线程同时,抢占锁的结果"); Thread thread1 = new Thread(()->{ testRedisLock(); }); thread1.setName("我是线程1"); Thread thread2 = new Thread(()->{ testRedisLock(); }); thread2.setName("我是线程2"); //同时启动线程 thread1.start(); thread2.start(); Thread.sleep(1000*20); System.out.println("-----------------我是一条分割线----------------"); System.out.println(""); System.out.println(""); System.out.println(""); System.out.println("下面是测试 一个线程获取锁成功后,由于业务执行时间超过了设置持有锁的时间,是否会把其他线程持有的锁给释放掉"); Thread thread3 = new Thread(()->{ testRedisLock2(); }); thread3.setName("我是线程3"); thread3.start(); Thread.sleep(1000*1);//暂停一秒是为了让线程3获的到锁 Thread thread4 = new Thread(()->{ testRedisLock(); }); thread4.setName("我是线程4"); thread4.start(); } public static void testRedisLock(){ LockParam lockParam = new LockParam(lockKey); lockParam.setTryLockTime(2000L);//2秒时间尝试获得锁 lockParam.setHoldLockTime(1000*10L);//获得锁成功后持有锁10秒时间 RedisLock redisLock = new RedisLock(lockParam); try { Boolean lockFlag = redisLock.lock(); System.out.println("加锁结果:{}"+lockFlag); if(lockFlag){ try { //20秒模拟处理业务代码时间 Thread.sleep(1000*5L); } catch (InterruptedException e) { e.printStackTrace(); } } }catch (Exception e) { System.out.println("testRedisLock e---->"+e); }finally { boolean unlockResp = redisLock.unlock(); System.out.println("释放锁结果:{}"+unlockResp); } } public static void testRedisLock2(){ LockParam lockParam = new LockParam(lockKey); lockParam.setTryLockTime(1000*2L);//2秒时间尝试获得锁 lockParam.setHoldLockTime(1000*2L);//获得锁成功后持有锁2秒时间 RedisLock redisLock = new RedisLock(lockParam); try { Boolean lockFlag = redisLock.lock(); System.out.println("加锁结果:{}"+lockFlag); if(lockFlag){ try { //10秒模拟处理业务代码时间 Thread.sleep(1000*10L); } catch (InterruptedException e) { e.printStackTrace(); } } }catch (Exception e) { System.out.println("testRedisLock e---->"+e); }finally { boolean unlockResp = redisLock.unlock(); System.out.println("释放锁结果:{}"+unlockResp); } } }
//pom.xml文件
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.cn</groupId> <artifactId>redisLock</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>war</packaging> <dependencies> <dependency> <groupId>org.redisson</groupId> <artifactId>redisson-spring-boot-starter</artifactId> <version>3.11.5</version> </dependency> <!-- --> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.9.3</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>RELEASE</version> <optional>true</optional> </dependency> </dependencies> <build> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.8</source> <target>1.8</target> <encoding>UTF-8</encoding> </configuration> </plugin> </plugins> </build> </project>