分布式锁
1 https://gitee.com/kekingcn/spring-boot-klock-starter
2 锁概念
2.1 公平锁:公平锁是指多个线程按照申请锁的顺序来获取锁,线程直接进入队列中排队,队列中的第一个线程才能获得锁。公平锁的优点是等待锁的线程不会饿死。缺点是整体吞吐效率相对非公平锁要低,等待队列中除第一个线程以外的所有线程都会阻塞,CPU唤醒阻塞线程的开销比非公平锁大。
2.2 非公平锁 多个线程加锁时直接尝试获取锁,获取不到才会到等待队列的队尾等待。但如果此时锁刚好可用,那么这个线程可以无需阻塞直接获取到锁,所以非公平锁有可能出现后申请锁的线程先获取锁的场景。非公平锁的优点是可以减少唤起线程的开销,整体的吞吐效率高,因为线程有几率不阻塞直接获得锁,CPU不必唤醒所有线程。缺点是处于等待队列中的线程可能会饿死,或者等很久才会获得锁。
2.3 可重入锁又名递归锁,是指在同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取锁。说的有点抽象,下面会有一个代码的示例。
2.4 读写锁(ReadWriteLock)管理一组锁,一个是只读的锁,一个是写锁。读锁可以在没有写锁的时候被多个线程同时持有,写锁是独占的。所有读写锁的实现必须确保写操作对读操作的内存影响。换句话说,一个获得了读锁的线程必须能看到前一个释放的写锁所更新的内容。读写锁比互斥锁允许对于共享数据更大程度的并发。每次只能有一个写线程,但是同时可以有多个线程并发地读数据。ReadWriteLock适用于读多写少的并发情况;
3 Klock支持的锁 可重入锁,公平锁,读锁,写锁
4 参数
name:lock的name,对应redis的key值。默认为:类名+方法名,建议这个值不要为空
lockType:锁的类型,目前支持(可重入锁,公平锁,读写锁)。默认为:公平锁
waitTime:获取锁最长等待时间。默认为:60s。这个值单位为second;
leaseTime:获得锁后,自动释放锁的时间。默认为:60s。同时也可通过spring.klock.leaseTime统一配置
lockTimeoutStrategy: 加锁超时的处理策略,可配置为不做处理、快速失败、阻塞等待的处理策略,默认策略为不做处理
customLockTimeoutStrategy: 自定义加锁超时的处理策略,需指定自定义处理的方法的方法名,并保持入参一致。
releaseTimeoutStrategy: 释放锁时,持有的锁已超时的处理策略,可配置为不做处理、快速失败的处理策略,默认策略为不做处理
customReleaseTimeoutStrategy: 自定义释放锁时,需指定自定义处理的方法的方法名,并保持入参一致。
策略包括:
NO_OPERATION,继续执行业务逻辑,不做任何处理,这种情况下可能会掉用失败
FAIL_FAST 快速失败,加锁或者释放锁超时,报错,报错逻辑自行处理
KEEP_ACQUIRE 一直阻塞,直到获得锁,在太多的尝试后,仍会报错
5 demo: 例子在demo工程下的LockController
@Override @Klock (name = "lockTest1" , keys = "#id" , waitTime = 2 , leaseTime = 200 , lockTimeoutStrategy = LockTimeoutStrategy.FAIL_FAST) public String lockMethod1(Integer id) { ThreadUtil.safeSleep( 100 * 1000 ); return ErrorCode.SUCCESS.getMessage(); } @Override @Klock (name = "lockTest1" , keys = { "#lockParam.id" , "#lockParam.name" }, waitTime = 2 , leaseTime = 2 , lockTimeoutStrategy = LockTimeoutStrategy.FAIL_FAST, releaseTimeoutStrategy = ReleaseTimeoutStrategy.NO_OPERATION ) public String lockMethod2(LockParam lockParam) { ThreadUtil.safeSleep( 100 * 1000 ); return ErrorCode.SUCCESS.getMessage(); } |
6 redisson Client 原生
@ApiOperation (value = "lock测试" ) @GetMapping (value = "/test/lock-redisson" ) public String lockRedisson( @RequestParam LockParam lockParam) { String lockKey = "lock:test:redisson" ; //非公平锁 RLock lock = redissonClient.getLock(lockKey); // 读写锁 ,参考 https://www.jianshu.com/p/9cd5212c8841 RReadWriteLock readWriteLock = redissonClient.getReadWriteLock(lockKey); //可重入公平锁 RLock fairLock = redissonClient.getFairLock(lockKey); try { boolean res = lock.tryLock( 10 , TimeUnit.SECONDS); if (res) { // to do somthing ThreadUtil.safeSleep( 2000 ); } } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } return "success" ; } |