Redisson——FairLock公平锁

# FairLock介绍

基于Redis的Redisson分布式可重入公平锁也是实现了java.util.concurrent.locks.Lock接口的一种RLock对象。同时还提供了异步(Async) (opens new window)反射式(Reactive) (opens new window)RxJava2标准 (opens new window)的接口。它保证了当多个Redisson客户端线程同时请求加锁时,优先分配给先发出请求的线程。所有请求线程会在一个队列中排队,当某个线程出现宕机时,Redisson会等待5秒后继续下一个线程,也就是说如果前面有5个线程都处于等待状态,那么后面的线程会等待至少25秒。

# FairLock使用

RLock fairLock = redisson.getFairLock("anyLock");
// 最常见的使用方法
fairLock.lock();
1
2
3

# FairLock看门狗机制

大家都知道,如果负责储存这个分布式锁的Redis节点宕机以后,而且这个锁正好处于锁住的状态时,这个锁会出现锁死的状态。为了避免这种情况的发生,Redisson内部提供了一个监控锁的看门狗,它的作用是在Redisson实例被关闭前,不断的延长锁的有效期。默认情况下,看门狗的检查锁的超时时间是30秒钟,也可以通过修改Config.lockWatchdogTimeout (opens new window)来另行指定。

另外Redisson还通过加锁的方法提供了leaseTime的参数来指定加锁的时间。超过这个时间后锁便自动解开了。

// 10秒钟以后自动解锁
// 无需调用unlock方法手动解锁
fairLock.lock(10, TimeUnit.SECONDS);

// 尝试加锁,最多等待100秒,上锁以后10秒自动解锁
boolean res = fairLock.tryLock(100, 10, TimeUnit.SECONDS);
...
fairLock.unlock();
1
2
3
4
5
6
7
8

# FairLock异步执行

RLock fairLock = redisson.getFairLock("anyLock");
fairLock.lockAsync();
fairLock.lockAsync(10, TimeUnit.SECONDS);
Future<Boolean> res = fairLock.tryLockAsync(100, 10, TimeUnit.SECONDS);
1
2
3
4

# 代码实现FairLock

编写接口代码:

@GetMapping("/test/fair/lock/{id}")
    public String testFairLock(@PathVariable("id") Long id) {
        stockService.testFairLock(id);
        return "hello test fairLock!";
    }
1
2
3
4
5

编写业务代码:

/**
     * 测试公平锁
     * @param id
     */
    public void testFairLock(Long id) {
        RLock fairLock = redissonClient.getFairLock("fairLock");
        fairLock.lock();
        try {
            TimeUnit.SECONDS.sleep(10);
           	System.out.println("........测试公平锁........"+id);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            fairLock.unlock();
        }
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 关于使用非公平锁导致请求多发的问题

假如我们使用的是FairLock,那么不会出现请求多发了一次的问题。当我们使用非公平锁,会出现插队(这是正常的)并且会有一些请求多发了一次,这是Nginx导致的,在Nginx中,对于很久都没有响应的请求,它会帮我们重发一次。

我们只需要修改一下nginx.conf配置文件,把响应时间设置长一点即可:

server {
        listen       80;
        server_name  localhost;
		
		#最大连接时间
		proxy_connect_timeout 12000;
		#最大发送等待时间
		proxy_send_timeout 12000;
		#最大接收等待时间
		proxy_read_timeout 12000;

        location / {
            proxy_pass http://ideaProject;
        }
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15