分布式锁
常见的三种:

基于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
| private StringRedisTemplate stringRedisTemplate;
private String name;
private static final String ID_PREFIX= UUID.randomUUID().toString(true)+"-";
private static final String KEY_PREFIX="lock:";
public SimpleRedisLock(StringRedisTemplate stringRedisTemplate, String name) { this.stringRedisTemplate = stringRedisTemplate; this.name = name; }
@Override public boolean tryLock(long timeoutSec) {
String threadId = ID_PREFIX +Thread.currentThread().getId();
Boolean success = stringRedisTemplate.opsForValue().setIfAbsent(KEY_PREFIX + name, threadId , timeoutSec, TimeUnit.SECONDS);
return Boolean.TRUE.equals(success); }
@Override public void unlock() {
String threadId = ID_PREFIX +Thread.currentThread().getId();
String id = stringRedisTemplate.opsForValue().get(KEY_PREFIX + name);
if(threadId.equals(id)){ stringRedisTemplate.delete(KEY_PREFIX+name); } }
|
极端情况下的误删

获取锁,执行业务皆成功,但是在释放锁时由于极端情况导致释放锁,被阻塞,如FULL GC,由于阻塞时间过长,触发了超时释放锁的机制,锁被释放,但是,线程1还是在阻塞中,在阻塞的这段时间内,恰好有另一个线程,获取了这把锁,并开始执行业务,在线程2执行业务期间,线程1阻塞结束,执行释放锁操作,就误删了线程2的锁。
出现这个情况的原因是:判断锁标识和释放锁的动作是分开的,两个动作。
原子操作的lua脚本

lua脚本,获取keys[1]的value,与正常的value比较,相同就调用delete方法,删除锁
1 2 3 4
| if(redis.call('get',KEYS[1])== ARGV[1]) then return redis.call('del',KEYS[1]) end return 0
|
利用静态代码块加载lua脚本
1 2 3 4 5 6 7
| private static final DefaultRedisScript<Long> UNLOCK_SCRIPT; static { UNLOCK_SCRIPT=new DefaultRedisScript<>(); UNLOCK_SCRIPT.setLocation(new ClassPathResource("unlock.lua")); UNLOCK_SCRIPT.setResultType(Long.class); }
|
调用lua脚本释放锁
1 2 3 4 5 6 7 8 9 10 11
| @Override public void unlock() { stringRedisTemplate.execute( UNLOCK_SCRIPT, Collections.singletonList(KEY_PREFIX + name), ID_PREFIX +Thread.currentThread().getId()); } }
|