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;
|
package cn.hutool.core.thread.lock;
|
||||||
|
|
||||||
import java.util.concurrent.Semaphore;
|
import java.util.concurrent.Semaphore;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.locks.Lock;
|
import java.util.concurrent.locks.Lock;
|
||||||
import java.util.concurrent.locks.ReadWriteLock;
|
import java.util.concurrent.locks.ReadWriteLock;
|
||||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||||
@ -140,4 +141,75 @@ public class LockUtil {
|
|||||||
public static Lock getLazySegmentLock(int segments, Object key) {
|
public static Lock getLazySegmentLock(int segments, Object key) {
|
||||||
return SegmentLock.lazyWeakLock(segments).get(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