mirror of
https://gitee.com/dromara/hutool.git
synced 2025-06-28 13:34:09 +08:00
Pre Merge pull request !1359 from wwwzzzggg/v5-dev
This commit is contained in:
commit
d15831e55a
@ -1,6 +1,7 @@
|
||||
package cn.hutool.core.thread.lock;
|
||||
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
@ -140,4 +141,75 @@ public class LockUtil {
|
||||
public static Lock getLazySegmentLock(int segments, Object key) {
|
||||
return SegmentLock.lazyWeakLock(segments).get(key);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 下面 tryLock 和 lock 静态方法和辅助类LockStat 实现锁的自动加锁和解锁的封装
|
||||
* 使用方法如下
|
||||
* <pre>
|
||||
* Lock myLock = new ReentrantLock();
|
||||
*
|
||||
* // 尝试获取锁,并设置超时时间
|
||||
* try (LockStat lockStat = LockUtils.tryLock(myLock, 5, TimeUnit.SECONDS)) {
|
||||
* if (lockStat.isLocked()) {
|
||||
* // Critical section - lock acquired successfully
|
||||
* doSomethingCritical();
|
||||
* } else {
|
||||
* // Handle case where lock was not acquired
|
||||
* handleLockTimeout();
|
||||
* }
|
||||
* } // Lock 会自动释放
|
||||
*
|
||||
* // 获取锁,不设置超时时间
|
||||
* try (LockStat lockStat = LockUtils.lock(myLock)) {
|
||||
* // Critical section - lock is guaranteed to be acquired
|
||||
* doSomethingCritical();
|
||||
* } // Lock 会自动释放
|
||||
* </pre>
|
||||
*
|
||||
*/
|
||||
public static LockStat tryLock(Lock lock, long timeout, TimeUnit timeUnit) {
|
||||
boolean tryLock = false;
|
||||
try {
|
||||
tryLock = lock.tryLock(timeout, timeUnit);
|
||||
} catch (InterruptedException e) {
|
||||
tryLock = false;
|
||||
}
|
||||
return new LockStat(lock, tryLock);
|
||||
}
|
||||
|
||||
public static LockStat tryLock(Lock lock) {
|
||||
boolean tryLock = lock.tryLock();
|
||||
return new LockStat(lock, tryLock);
|
||||
}
|
||||
|
||||
public static LockStat lock(Lock lock) {
|
||||
lock.lock();
|
||||
return new LockStat(lock, true);
|
||||
}
|
||||
|
||||
public static class LockStat implements AutoCloseable {
|
||||
private final Lock lock;
|
||||
private final boolean isLocked;
|
||||
|
||||
public LockStat(Lock lock, boolean isLocked) {
|
||||
this.lock = lock;
|
||||
this.isLocked = isLocked;
|
||||
}
|
||||
|
||||
public Lock getLock() {
|
||||
return lock;
|
||||
}
|
||||
|
||||
public boolean isLocked() {
|
||||
return isLocked;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (isLocked) {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,228 @@
|
||||
package cn.hutool.core.thread.lock;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* LockUtil单元测试类
|
||||
*/
|
||||
class LockUtilTest {
|
||||
|
||||
private Lock testLock;
|
||||
private ExecutorService executor;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
testLock = new ReentrantLock();
|
||||
executor = Executors.newCachedThreadPool();
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试tryLock方法 - 立即获取锁成功
|
||||
*/
|
||||
@Test
|
||||
void testTryLockSuccess() {
|
||||
try (LockUtil.LockStat lockStat = LockUtil.tryLock(testLock)) {
|
||||
assertTrue(lockStat.isLocked(), "tryLock应该成功获取锁");
|
||||
assertSame(testLock, lockStat.getLock(), "返回的锁应该是传入的锁对象");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试tryLock方法 - 立即获取锁失败
|
||||
*/
|
||||
@Test
|
||||
void testTryLockFail() throws InterruptedException, ExecutionException, TimeoutException {
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
CountDownLatch releaseLatch = new CountDownLatch(1);
|
||||
|
||||
// 在另一个线程中先获取锁
|
||||
Future<Void> future = executor.submit(() -> {
|
||||
testLock.lock();
|
||||
latch.countDown(); // 通知已获取锁
|
||||
try {
|
||||
releaseLatch.await(); // 等待释放信号
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
} finally {
|
||||
testLock.unlock();
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
// 等待另一个线程获取锁
|
||||
latch.await();
|
||||
|
||||
// 尝试获取锁应该失败
|
||||
try (LockUtil.LockStat lockStat = LockUtil.tryLock(testLock)) {
|
||||
assertFalse(lockStat.isLocked(), "tryLock应该获取锁失败");
|
||||
assertSame(testLock, lockStat.getLock(), "返回的锁应该是传入的锁对象");
|
||||
}
|
||||
|
||||
// 释放第一个线程的锁
|
||||
releaseLatch.countDown();
|
||||
future.get(1, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试tryLock方法 - 带超时时间成功获取锁
|
||||
*/
|
||||
@Test
|
||||
void testTryLockWithTimeoutSuccess() {
|
||||
try (LockUtil.LockStat lockStat = LockUtil.tryLock(testLock, 1, TimeUnit.SECONDS)) {
|
||||
assertTrue(lockStat.isLocked(), "tryLock应该在超时时间内成功获取锁");
|
||||
assertSame(testLock, lockStat.getLock(), "返回的锁应该是传入的锁对象");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试tryLock方法 - 带超时时间获取锁超时
|
||||
*/
|
||||
@Test
|
||||
void testTryLockWithTimeoutFail() throws InterruptedException, ExecutionException, TimeoutException {
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
CountDownLatch releaseLatch = new CountDownLatch(1);
|
||||
|
||||
// 在另一个线程中先获取锁并持有较长时间
|
||||
Future<Void> future = executor.submit(() -> {
|
||||
testLock.lock();
|
||||
latch.countDown(); // 通知已获取锁
|
||||
try {
|
||||
releaseLatch.await(2, TimeUnit.SECONDS); // 持有锁2秒
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
} finally {
|
||||
testLock.unlock();
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
// 等待另一个线程获取锁
|
||||
latch.await();
|
||||
|
||||
// 尝试在短时间内获取锁应该超时失败
|
||||
long startTime = System.currentTimeMillis();
|
||||
try (LockUtil.LockStat lockStat = LockUtil.tryLock(testLock, 100, TimeUnit.MILLISECONDS)) {
|
||||
assertFalse(lockStat.isLocked(), "tryLock应该因超时而获取锁失败");
|
||||
assertSame(testLock, lockStat.getLock(), "返回的锁应该是传入的锁对象");
|
||||
}
|
||||
long elapsedTime = System.currentTimeMillis() - startTime;
|
||||
assertTrue(elapsedTime >= 100 && elapsedTime < 500, "应该在大约100毫秒后超时");
|
||||
|
||||
// 释放第一个线程的锁
|
||||
releaseLatch.countDown();
|
||||
future.get(3, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试tryLock方法 - 处理InterruptedException
|
||||
*/
|
||||
@Test
|
||||
void testTryLockInterrupted() throws InterruptedException {
|
||||
CountDownLatch startLatch = new CountDownLatch(1);
|
||||
CountDownLatch lockAcquiredLatch = new CountDownLatch(1);
|
||||
CountDownLatch testCompleteLatch = new CountDownLatch(1);
|
||||
|
||||
// 先在主线程获取锁
|
||||
testLock.lock();
|
||||
|
||||
Future<Boolean> future = executor.submit(() -> {
|
||||
startLatch.countDown();
|
||||
try (LockUtil.LockStat lockStat = LockUtil.tryLock(testLock, 5, TimeUnit.SECONDS)) {
|
||||
return lockStat.isLocked();
|
||||
} finally {
|
||||
testCompleteLatch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
// 等待子线程开始执行
|
||||
startLatch.await();
|
||||
Thread.sleep(100); // 确保子线程已开始等待锁
|
||||
|
||||
// 中断子线程
|
||||
future.cancel(true);
|
||||
|
||||
// 释放锁
|
||||
testLock.unlock();
|
||||
|
||||
// 等待子线程完成
|
||||
testCompleteLatch.await(1, TimeUnit.SECONDS);
|
||||
|
||||
try {
|
||||
assertTrue(future.isCancelled() || !future.get(100, TimeUnit.MILLISECONDS),
|
||||
"被中断的线程应该获取锁失败");
|
||||
} catch (ExecutionException | TimeoutException e) {
|
||||
// 预期的异常,因为线程被中断
|
||||
assertTrue(future.isCancelled(), "线程应该被取消");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试lock方法 - 成功获取锁
|
||||
*/
|
||||
@Test
|
||||
void testLockSuccess() {
|
||||
try (LockUtil.LockStat lockStat = LockUtil.lock(testLock)) {
|
||||
assertTrue(lockStat.isLocked(), "lock应该成功获取锁");
|
||||
assertSame(testLock, lockStat.getLock(), "返回的锁应该是传入的锁对象");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 测试LockStat的close方法只在获取锁成功时才释放锁
|
||||
*/
|
||||
@Test
|
||||
void testLockStatCloseOnlyWhenLocked() {
|
||||
try (LockUtil.LockStat lockStat = LockUtil.tryLock(testLock)) {
|
||||
assertTrue(lockStat.isLocked(), "重入锁获取锁成功");
|
||||
} // close方法不应该调用unlock,因为没有获取到锁
|
||||
|
||||
|
||||
// 测试获取锁成功的情况
|
||||
try (LockUtil.LockStat lockStat = LockUtil.tryLock(testLock)) {
|
||||
assertTrue(lockStat.isLocked(), "应该获取锁成功");
|
||||
} // close方法应该调用unlock
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试多次close调用的安全性
|
||||
*/
|
||||
@Test
|
||||
void testMultipleClose() {
|
||||
LockUtil.LockStat lockStat = LockUtil.lock(testLock);
|
||||
assertTrue(lockStat.isLocked(), "应该获取锁成功");
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试嵌套使用try-with-resources
|
||||
*/
|
||||
@Test
|
||||
void testNestedTryWithResources() {
|
||||
ReentrantLock reentrantLock = new ReentrantLock();
|
||||
|
||||
try (LockUtil.LockStat outerLockStat = LockUtil.lock(reentrantLock)) {
|
||||
assertTrue(outerLockStat.isLocked(), "外层应该获取锁成功");
|
||||
|
||||
// 由于ReentrantLock支持重入,内层也应该成功
|
||||
try (LockUtil.LockStat innerLockStat = LockUtil.lock(reentrantLock)) {
|
||||
assertTrue(innerLockStat.isLocked(), "内层应该获取锁成功(重入)");
|
||||
}
|
||||
|
||||
// 外层锁仍应该有效
|
||||
assertTrue(outerLockStat.isLocked(), "外层锁仍应该有效");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user