Distributed lock 2 in redisson

Fair Lock

The Redisson distributed reentrant fair lock based on Redis is also a RLock object that implements the java.util.concurrent.locks.Lock interface. It also provides asynchronous (Async), reflective (Reactive) and RxJava2 standard interfaces. It ensures that when multiple Redisson client threads request locks at the same time, priority will be given to the thread that made the request first. All request threads will be queued in a queue. When a thread goes down, Redisson will wait for 5 seconds before continuing to the next thread. That is to say, if the previous 5 threads are waiting, then the following threads will wait. At least 25 seconds.

RLock fairLock = redisson.getFairLock("anyLock");
//The most common usage
fairLock.lock();

// Automatically unlock after 10 seconds
// No need to call the unlock method to manually unlock
fairLock.lock(10, TimeUnit.SECONDS);

//Try to lock, wait up to 100 seconds, and automatically unlock 10 seconds after locking
boolean res = fairLock.tryLock(100, 10, TimeUnit.SECONDS);
fairLock.unlock();

Interlock (MultiLock)

The Redisson distributed interlock RedissonMultiLock object based on Redis can associate multiple RLock objects into an interlock. Each RLock object instance can come from for different Redisson instances.

RLock lock1 = redissonInstance1.getLock("lock1");
RLock lock2 = redissonInstance2.getLock("lock2");
RLock lock3 = redissonInstance3.getLock("lock3");

RedissonMultiLock lock = new RedissonMultiLock(lock1, lock2, lock3);
// Lock simultaneously: lock1 lock2 lock3
// Success is considered successful if all locks are successfully locked.
lock.lock();
...
lock.unlock();

RedLock

The Redisson red lock RedissonRedLock object based on Redis implements the locking algorithm introduced by Redlock. This object can also be used to associate multiple RLock objects into a red lock. Each RLock object instance can come from a different Redisson instance.

RLock lock1 = redissonInstance1.getLock("lock1");
RLock lock2 = redissonInstance2.getLock("lock2");
RLock lock3 = redissonInstance3.getLock("lock3");

RedissonRedLock lock = new RedissonRedLock(lock1, lock2, lock3);
// Lock simultaneously: lock1 lock2 lock3
// The red lock is considered successful if it is successfully locked on most nodes.
lock.lock();
...
lock.unlock();

Read-write lock (ReadWriteLock)

The Redisson distributed reentrant read-write lock based on Redis RReadWriteLock Java object implements the java.util.concurrent.locks.ReadWriteLock interface. Both read locks and write locks inherit the RLock interface.

Distributed reentrant read-write locks allow multiple read locks and one write lock to be locked at the same time.

RReadWriteLock rwlock = redisson.getReadWriteLock("anyRWLock");
//The most common usage
rwlock.readLock().lock();
// or
rwlock.writeLock().lock();

// Automatically unlock after 10 seconds
// No need to call the unlock method to manually unlock
rwlock.readLock().lock(10, TimeUnit.SECONDS);
// or
rwlock.writeLock().lock(10, TimeUnit.SECONDS);

//Try to lock, wait up to 100 seconds, and automatically unlock 10 seconds after locking
boolean res = rwlock.readLock().tryLock(100, 10, TimeUnit.SECONDS);
// or
boolean res = rwlock.writeLock().tryLock(100, 10, TimeUnit.SECONDS);
...
lock.unlock();

Add StockController method:

@GetMapping("test/read")
public String testRead(){<!-- -->
    String msg = stockService.testRead();

    return "Test read";
}

@GetMapping("test/write")
public String testWrite(){<!-- -->
    String msg = stockService.testWrite();

    return "Test writing";
}

Add StockService method:

public String testRead() {<!-- -->
    RReadWriteLock rwLock = this.redissonClient.getReadWriteLock("rwLock");
    rwLock.readLock().lock(10, TimeUnit.SECONDS);

    System.out.println("Test read lock...");
    // rwLock.readLock().unlock();

    return null;
}

public String testWrite() {<!-- -->
    RReadWriteLock rwLock = this.redissonClient.getReadWriteLock("rwLock");
    rwLock.writeLock().lock(10, TimeUnit.SECONDS);

    System.out.println("Test write lock...");
    // rwLock.writeLock().unlock();

    return null;
}

Open two browser windows to test:

  • Simultaneous access writing: After one writing is completed, wait for a while (about 10s), and the other writing starts
  • Simultaneous access to read: no need to wait
  • Write first and then read: Read and wait (about 10s) for writing to complete.
  • Read first and then write: write and wait (about 10s) for reading to complete.

Semaphore

Redisson’s distributed semaphore (Semaphore) Java object RSemaphore based on Redis adopts an interface and usage similar to java.util.concurrent.Semaphore. It also provides asynchronous (Async), reflective (Reactive) and RxJava2 standard interfaces.

RSemaphore semaphore = redisson.getSemaphore("semaphore");
semaphore.trySetPermits(3);
semaphore.acquire();
semaphore.release();

Add method to StockController:

@GetMapping("test/semaphore")
public String testSemaphore(){<!-- -->
    this.stockService.testSemaphore();

    return "Test semaphore";
}

Add method in StockService:

public void testSemaphore() {<!-- -->
    RSemaphore semaphore = this.redissonClient.getSemaphore("semaphore");
    semaphore.trySetPermits(3);
    try {<!-- -->
        semaphore.acquire();

        TimeUnit.SECONDS.sleep(5);
        System.out.println(System.currentTimeMillis());

        semaphore.release();
    } catch (InterruptedException e) {<!-- -->
        e.printStackTrace();
    }
}

Add test case: concurrency 10 times, loop once

Console effect:

Console 1:
1606960790234
1606960800337
1606960800443
1606960805248

Console 2:
1606960790328
1606960795332
1606960800245

Console 3:
1606960790433
1606960795238
1606960795437

It can be seen from this:

3 requests came in during 1606960790 seconds: 1 for each console

There were 3 requests coming in during 1606960795 seconds: 1 request from console 2 and 2 requests from console 3.

3 requests came in during 1606960800 seconds: 2 requests from console 1 and 1 request from console 2.

1 request came in in 1606960805 seconds: 1 request from console 1

Latch (CountDownLatch)

The Redisson distributed locking (CountDownLatch) Java object RCountDownLatch based on Redisson adopts an interface and usage similar to java.util.concurrent.CountDownLatch.

RCountDownLatch latch = redisson.getCountDownLatch("anyCountDownLatch");
latch.trySetCount(1);
latch.await();

// In other threads or other JVMs
RCountDownLatch latch = redisson.getCountDownLatch("anyCountDownLatch");
latch.countDown();

Two methods are needed: one waiting and one counting countDown

Add a test method to StockController:

@GetMapping("test/latch")
public String testLatch(){<!-- -->
    this.stockService.testLatch();

    return "The squad leader locked the door...";
}

@GetMapping("test/countdown")
public String testCountDown(){<!-- -->
    this.stockService.testCountDown();

    return "A classmate came out";
}

Add a test method to StockService:

public void testLatch() {<!-- -->
    RCountDownLatch latch = this.redissonClient.getCountDownLatch("latch");
    latch.trySetCount(6);

    try {<!-- -->
        latch.await();
    } catch (InterruptedException e) {<!-- -->
        e.printStackTrace();
    }
}

public void testCountDown() {<!-- -->
    RCountDownLatch latch = this.redissonClient.getCountDownLatch("latch");
    latch.trySetCount(6);

    latch.countDown();
}

Restart the test and open two pages: the first request will not be executed until the second request is executed 6 times.