更多的时候,我们会选用带等待时间周期和锁最大持有时间的API:
@Test public void testTryLockAndUnLock() throws Exception { String resourceName = "resource:x"; int waitTime = 500; int leaseTime = 1000; Thread threadA = new Thread(() -> { process(resourceName, waitTime, leaseTime); }, "threadA"); Thread threadB = new Thread(() -> { process(resourceName, waitTime, leaseTime); }, "threadB"); threadA.start(); threadB.start(); Thread.sleep(Long.MAX_VALUE); } private void process(String resourceName, int waitTime, int leaseTime) { RLock lock = REDISSON.getLock(resourceName); try { String threadName = Thread.currentThread().getName(); boolean tryLock = lock.tryLock(waitTime, leaseTime, TimeUnit.MILLISECONDS); if (tryLock) { try { System.out.println(String.format("线程%s获取到资源%s的锁", threadName, resourceName)); Thread.sleep(800); } finally { lock.unlock(); System.out.println(String.format("线程%s释放资源%s的锁", Thread.currentThread().getName(), resourceName)); } } else { System.out.println(String.format("线程%s获取资源%s的锁失败,等待时间:%d ms", threadName, resourceName, waitTime)); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } // 某次执行的输出结果 线程threadA获取到资源resource:x的锁 线程threadB获取资源resource:x的锁失败,等待时间:500 ms 线程threadA释放资源resource:x的锁为了使用的时候更加简单,可以参考spring-tx中的编程式事务那样进行轻度封装:
@RequiredArgsConstructor private static class RedissonLockProvider { private final RedissonClient redissonClient; public <T> T executeInLock(String resourceName, LockAction lockAction) { RLock lock = redissonClient.getLock(resourceName); try { lock.lock(); lockAction.onAcquire(resourceName); return lockAction.doInLock(resourceName); } finally { lock.unlock(); lockAction.onExit(resourceName); } } public <T> T executeInLock(String resourceName, int waitTime, int leaseTime, LockAction lockAction) throws InterruptedException { RLock lock = redissonClient.getLock(resourceName); boolean tryLock = lock.tryLock(waitTime, leaseTime, TimeUnit.MILLISECONDS); if (tryLock) { try { lockAction.onAcquire(resourceName); return lockAction.doInLock(resourceName); } finally { lock.unlock(); lockAction.onExit(resourceName); } } return null; } public void executeInLockWithoutResult(String resourceName, int waitTime, int leaseTime, LockActionWithoutResult lockAction) throws InterruptedException { RLock lock = redissonClient.getLock(resourceName); boolean tryLock = lock.tryLock(waitTime, leaseTime, TimeUnit.MILLISECONDS); if (tryLock) { try { lockAction.onAcquire(resourceName); lockAction.doInLock(resourceName); } finally { lock.unlock(); lockAction.onExit(resourceName); } } } public void executeInLockWithoutResult(String resourceName, LockActionWithoutResult lockAction) { RLock lock = redissonClient.getLock(resourceName); try { lock.lock(); lockAction.onAcquire(resourceName); lockAction.doInLock(resourceName); } finally { lock.unlock(); lockAction.onExit(resourceName); } } } @FunctionalInterface interface LockAction { default void onAcquire(String resourceName) { } <T> T doInLock(String resourceName); default void onExit(String resourceName) { } } @FunctionalInterface interface LockActionWithoutResult { default void onAcquire(String resourceName) { } void doInLock(String resourceName); default void onExit(String resourceName) { } }使用RedissonLockProvider(仅供参考):
@Test public void testRedissonLockProvider() throws Exception { RedissonLockProvider provider = new RedissonLockProvider(REDISSON); String resourceName = "resource:x"; Thread threadA = new Thread(() -> { provider.executeInLockWithoutResult(resourceName, new LockActionWithoutResult() { @Override public void onAcquire(String resourceName) { System.out.println(String.format("线程%s获取到资源%s的锁", Thread.currentThread().getName(), resourceName)); } @Override public void doInLock(String resourceName) { try { Thread.sleep(800); } catch (InterruptedException ignore) { } } @Override public void onExit(String resourceName) { System.out.println(String.format("线程%s释放资源%s的锁", Thread.currentThread().getName(), resourceName)); } }); }, "threadA"); Thread threadB = new Thread(() -> { provider.executeInLockWithoutResult(resourceName, new LockActionWithoutResult() { @Override public void onAcquire(String resourceName) { System.out.println(String.format("线程%s获取到资源%s的锁", Thread.currentThread().getName(), resourceName)); } @Override public void doInLock(String resourceName) { try { Thread.sleep(800); } catch (InterruptedException ignore) { } } @Override public void onExit(String resourceName) { System.out.println(String.format("线程%s释放资源%s的锁", Thread.currentThread().getName(), resourceName)); } }); }, "threadB"); threadA.start(); threadB.start(); Thread.sleep(Long.MAX_VALUE); } // 某次执行结果 线程threadA获取到资源resource:x的锁 线程threadA释放资源resource:x的锁 线程threadB获取到资源resource:x的锁 线程threadB释放资源resource:x的锁 Redisson中RLock的实现原理Redisson中RLock的实现是基本参照了Redis的red lock算法进行实现,不过在原始的red lock算法下进行了改良,主要包括下面的特性:
互斥
无死锁
可重入,类似于ReentrantLock,同一个线程可以重复获取同一个资源的锁(一般使用计数器实现),锁的重入特性一般情况下有利于提高资源的利用率