1. Introduction to redlock
Distributed locking is a very useful technical means when different processes need to access shared resources mutually exclusively. There are three properties to consider when implementing efficient distributed locks:
- Security attribute: Mutual Exclusion, no matter when, only one client holds the lock
- Efficiency attribute A: No deadlock
- Efficiency attribute B: Fault tolerance. As long as most redis nodes can work normally, the client can acquire and release the lock.
Redlock is an algorithm that implements a distributed lock manager proposed by Redis. This algorithm will be safer and more reliable than ordinary methods. For discussion of this algorithm, please read the official documentation.
2. How to use redlock in java
Introduce redis and redisson dependencies in pom file:
<!-- redis--> <dependency> <groupId></groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!-- redisson--> <dependency> <groupId></groupId> <artifactId>redisson</artifactId> <version>3.3.2</version> </dependency>
The AquiredLockWorker interface class is mainly used for logic that needs to be processed after acquiring the lock:
/** * Created by fangzhipeng on 2017/4/5. * Logic that needs to be processed after acquiring the lock */ public interface AquiredLockWorker<T> { T invokeAfterLockAquire() throws Exception; }
DistributedLocker acquires lock management class:
/** * Created by fangzhipeng on 2017/4/5. * Acquire lock management class */ public interface DistributedLocker { /** * Acquire the lock * @param resourceName The name of the lock * @param worker Processing class after obtaining lock * @param <T> * @return The data to be returned after processing the specific business logic * @throws UnableToAquireLockException * @throws Exception */ <T> T lock(String resourceName, AquiredLockWorker<T> worker) throws UnableToAquireLockException, Exception; <T> T lock(String resourceName, AquiredLockWorker<T> worker, int lockTime) throws UnableToAquireLockException, Exception; }
UnableToAquireLockException , the exception class that cannot be acquired:
/** * Created by fangzhipeng on 2017/4/5. *Exception class */ public class UnableToAquireLockException extends RuntimeException { public UnableToAquireLockException() { } public UnableToAquireLockException(String message) { super(message); } public UnableToAquireLockException(String message, Throwable cause) { super(message, cause); } }
RedissonConnector Connection Class:
/** * Created by fangzhipeng on 2017/4/5. * Get the RedissonClient connection class */ @Component public class RedissonConnector { RedissonClient redisson; @PostConstruct public void init(){ redisson = (); } public RedissonClient getClient(){ return redisson; } }
The RedisLocker class implements DistributedLocker:
import ; import ; import ; import ; import ; /** * Created by fangzhipeng on 2017/4/5. */ @Component public class RedisLocker implements DistributedLocker{ private final static String LOCKER_PREFIX = "lock:"; @Autowired RedissonConnector redissonConnector; @Override public <T> T lock(String resourceName, AquiredLockWorker<T> worker) throws InterruptedException, UnableToAquireLockException, Exception { return lock(resourceName, worker, 100); } @Override public <T> T lock(String resourceName, AquiredLockWorker<T> worker, int lockTime) throws UnableToAquireLockException, Exception { RedissonClient redisson= (); RLock lock = (LOCKER_PREFIX + resourceName); // Wait for 100 seconds seconds and automatically unlock it after lockTime seconds boolean success = (100, lockTime, ); if (success) { try { return (); } finally { (); } } throw new UnableToAquireLockException(); } }
Test class:
@Autowired RedisLocker distributedLocker; @RequestMapping(value = "/redlock") public String testRedlock() throws Exception{ CountDownLatch startSignal = new CountDownLatch(1); CountDownLatch doneSignal = new CountDownLatch(5); for (int i = 0; i < 5; ++i) { // create and start threads new Thread(new Worker(startSignal, doneSignal)).start(); } (); // let all threads proceed (); ("All processors done. Shutdown connection"); return "redlock"; } class Worker implements Runnable { private final CountDownLatch startSignal; private final CountDownLatch doneSignal; Worker(CountDownLatch startSignal, CountDownLatch doneSignal) { = startSignal; = doneSignal; } public void run() { try { (); ("test",new AquiredLockWorker<Object>() { @Override public Object invokeAfterLockAquire() { doTask(); return null; } }); }catch (Exception e){ } } void doTask() { (().getName() + " start"); Random random = new Random(); int _int = (200); (().getName() + " sleep " + _int + "millis"); try { (_int); } catch (InterruptedException e) { (); } (().getName() + " end"); (); } }
Run the test class:
Thread-48 start Thread-48 sleep 99millis Thread-48 end Thread-49 start Thread-49 sleep 118millis Thread-49 end Thread-52 start Thread-52 sleep 141millis Thread-52 end Thread-50 start Thread-50 sleep 28millis Thread-50 end Thread-51 start Thread-51 sleep 145millis Thread-51 end
Judging from the running results, in the case of asynchronous tasks, the thread can indeed be run after acquiring the lock. In any case, this is a solution officially recommended by Redis, which is relatively reliable. If you have any questions, please leave a message.
3. Reference materials
/redisson/redisson
The above is all the content of this article. I hope it will be helpful to everyone's study and I hope everyone will support me more.