带有 CompletableFuture 的 ReentrantLock
ReentrantLock with CompletableFuture
在 CompletableFuture 的世界之前,如果我想锁定一个变量,我可以这样做:
private ReentrantLock myLock = new ReentrantLock();
private List<UUID> myLockableList = new ArrayList<>();
public void doStuff()
{
try
{
myLock.lock();
myLockableList.clear();
}
finally
{
if (myLock.isLocked())
{
myLock.unlock();
}
}
}
(显然,这是一个非常简化的示例,我也有其他方法尝试对 myLockableList 进行操作)。
根据 ReentrantLock,会发生 3 种情况中的 1 种:
- 如果锁没有被另一个线程持有则获取锁并立即returns,将锁持有计数设置为一个
- 如果当前线程已经持有锁,则持有计数递增 1 并且方法 returns 立即
- 如果锁由另一个线程持有,则当前线程将因线程调度目的而被禁用并处于休眠状态,直到获得锁,此时锁持有计数设置为 1。
一切都很好,正是我所期望的行为方式:如果在同一个线程中工作,我应该知道我是否锁定了资源,如果另一个线程锁定了资源,我想等到它可用。
输入 CompletableFutures...
private ReentrantLock myLock = new ReentrantLock();
private List<UUID> myLockableList = new ArrayList<>();
public CompletionStage<Void> doStuff()
{
return CompletableFuture.runAsync(() -> {
try
{
myLock.lock();
myLockableList.clear();
}
finally
{
if (myLock.isLocked())
{
myLock.unlock();
}
}
});
}
我希望 CompletableFuture 的行为是一样的。但是,CompletableFuture 可能会使用新线程执行上述操作,或者(如果我的理解是正确的)它可能会使用已在使用的线程。如果发生第二种情况(被重用的线程已经获得了锁),那么我的锁不会暂停线程并等待锁,相反,它实际上可能 return 立即(看起来它已经获得了锁)!
我似乎无法在文档中找到明确的答案。我是否正确理解了情况?如果是这样,我们应该如何在使用 CompletableFuture 时锁定资源?
非常感谢任何建议!
此外 concurrency tutorial recommended in the comments, the Javadoc for ReentrantLock 还提供了值得一读的重要信息。
这个,例如:
A ReentrantLock is owned by the thread last successfully locking, but not yet unlocking it.
你的例子使用了 isLocked(),Javadoc 说你不应该这样做:
This method is designed for use in monitoring of the system state, not for synchronization control.
最后,Javadoc 包含一个很好的用法示例,显示 "try" 块中的 lock()
后跟 "finally" 块中的 unlock()
。
It is recommended practice to always immediately follow a call to lock with a try block, most typically in a before/after construction such as:
class X {
private final ReentrantLock lock = new ReentrantLock();
// ...
public void m() {
lock.lock(); // block until condition holds
try {
// ... method body
} finally {
lock.unlock()
}
}
}
在 CompletableFuture 的世界之前,如果我想锁定一个变量,我可以这样做:
private ReentrantLock myLock = new ReentrantLock();
private List<UUID> myLockableList = new ArrayList<>();
public void doStuff()
{
try
{
myLock.lock();
myLockableList.clear();
}
finally
{
if (myLock.isLocked())
{
myLock.unlock();
}
}
}
(显然,这是一个非常简化的示例,我也有其他方法尝试对 myLockableList 进行操作)。
根据 ReentrantLock,会发生 3 种情况中的 1 种:
- 如果锁没有被另一个线程持有则获取锁并立即returns,将锁持有计数设置为一个
- 如果当前线程已经持有锁,则持有计数递增 1 并且方法 returns 立即
- 如果锁由另一个线程持有,则当前线程将因线程调度目的而被禁用并处于休眠状态,直到获得锁,此时锁持有计数设置为 1。
一切都很好,正是我所期望的行为方式:如果在同一个线程中工作,我应该知道我是否锁定了资源,如果另一个线程锁定了资源,我想等到它可用。
输入 CompletableFutures...
private ReentrantLock myLock = new ReentrantLock();
private List<UUID> myLockableList = new ArrayList<>();
public CompletionStage<Void> doStuff()
{
return CompletableFuture.runAsync(() -> {
try
{
myLock.lock();
myLockableList.clear();
}
finally
{
if (myLock.isLocked())
{
myLock.unlock();
}
}
});
}
我希望 CompletableFuture 的行为是一样的。但是,CompletableFuture 可能会使用新线程执行上述操作,或者(如果我的理解是正确的)它可能会使用已在使用的线程。如果发生第二种情况(被重用的线程已经获得了锁),那么我的锁不会暂停线程并等待锁,相反,它实际上可能 return 立即(看起来它已经获得了锁)!
我似乎无法在文档中找到明确的答案。我是否正确理解了情况?如果是这样,我们应该如何在使用 CompletableFuture 时锁定资源?
非常感谢任何建议!
此外 concurrency tutorial recommended in the comments, the Javadoc for ReentrantLock 还提供了值得一读的重要信息。
这个,例如:
A ReentrantLock is owned by the thread last successfully locking, but not yet unlocking it.
你的例子使用了 isLocked(),Javadoc 说你不应该这样做:
This method is designed for use in monitoring of the system state, not for synchronization control.
最后,Javadoc 包含一个很好的用法示例,显示 "try" 块中的 lock()
后跟 "finally" 块中的 unlock()
。
It is recommended practice to always immediately follow a call to lock with a try block, most typically in a before/after construction such as:
class X {
private final ReentrantLock lock = new ReentrantLock();
// ...
public void m() {
lock.lock(); // block until condition holds
try {
// ... method body
} finally {
lock.unlock()
}
}
}