Pre Merge pull request !1359 from wwwzzzggg/v5-dev

This commit is contained in:
wwwzzzggg 2025-06-09 03:44:29 +00:00 committed by Gitee
commit d15831e55a
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
2 changed files with 300 additions and 0 deletions

View File

@ -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();
}
}
}
}

View File

@ -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(), "外层锁仍应该有效");
}
}
}