基于redis的分布式锁

锁实现代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
public class RedisLock {

private final Logger log = LoggerFactory.getLogger(RedisLock.class);
private static final Long RELEASE_SUCCESS = 1L;

private String lockKey;
private RedisConnectionFactory connectionFactory;

public RedisLock(RedisConnectionFactory connectionFactory, String lockKey) {
this.connectionFactory = connectionFactory;
this.lockKey = lockKey;
}
/**
* 加锁
* 取到锁加锁,并返回值用于解锁;取不到锁则立即返回-1
* @param milliTimeout 最长锁定时间,超时后自动删除锁,避免死锁
* @return
*/
public synchronized long lock(long milliTimeout) {
log.debug("开始执行加锁 ==> {}",lockKey);
try (Jedis jedis = getJedis()) {
/**
* 生成随机数,用于解锁
* 保证解锁的线程和加锁的线程为同一个线程
*/
long lockValue = new Random(System.currentTimeMillis()).nextLong();

// 保存成功返回"OK",否则返回null
String result = jedis.set(lockKey, String.valueOf(lockValue), "NX", "PX", milliTimeout);
if("OK".equals(result)) {
return lockValue;
}
} catch (Exception e) {
throw new RuntimeException(e);
}
return -1;
}
/**
* 解锁
* @param lockValue
*/
public synchronized boolean unlock(Long lockValue) {
boolean unlockSuccess = false;
log.debug("执行解锁 ==> {}",lockKey);
try (Jedis jedis = getJedis()) {
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
Object result = jedis.eval(script, 1, lockKey.toString(), lockValue.toString());
if (RELEASE_SUCCESS.equals(result)) {
log.debug("解锁成功 ==> {}",lockKey);
unlockSuccess = true;
}
} catch (Exception e) {
log.error(e.getMessage(),e);
throw new RuntimeException(e);
}
return unlockSuccess;
}

/**
* springboot中通过反射从RedisConnectionFactory中获取jedis,
* 因为redisTemplate封装set(key, value, "NX", "PX", milliTimeout)接口时没有返回值
*/
private Jedis getJedis() {
Field jedisField = ReflectionUtils.findField(JedisConnection.class, "jedis");
ReflectionUtils.makeAccessible(jedisField);
Jedis jedis = (Jedis) ReflectionUtils.getField(jedisField, connectionFactory.getConnection());
return jedis;
}

}

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

@Autowired
private RedisConnectionFactory connectionFactory;

public void testLock() {

// redis分布式锁
String key = "testLock-redis-lock";
RedisLock lock = new RedisLock(connectionFactory, key);

// 取不到锁立即返回-1
long value = lock.lock(10000);
if(value != -1) {
log.debug("竞争锁成功 ==> {}",key);
try {
// TODO
} catch (Exception e) {
log.error(e.getMessage(), e);
} finally {
lock.unlock(value);
}
} else {
log.debug("竞争锁失败 ==> {}",key);
}
}

redisson

采用成熟的框架redisson,封装好的方法则可以使用。

github

文档

分布式锁和同步器