【发布时间】:2020-01-14 11:24:58
【问题描述】:
在 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 个会发生:
- 如果没有被其他线程持有,则获取锁并立即返回,将锁持有计数设置为 1
- 如果当前线程已经持有锁,那么持有计数加一并且方法立即返回
- 如果锁由另一个线程持有,则当前线程将出于线程调度目的而被禁用并处于休眠状态,直到获得锁为止,此时锁持有计数设置为 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 的行为是相同的。但是,A CompletableFuture 可能会使用新线程执行上述操作,或者(如果我的理解是正确的)它可能会使用已经在使用的线程。如果第二个发生(被重用的线程已经获得了锁),那么我的锁不会暂停线程并等待锁,相反,它实际上可能立即返回(看起来它已经获得了锁)!
我似乎无法在文档中找到明确的答案。我是否正确理解了情况?如果是这样,我们在使用 CompletableFuture 时应该如何锁定资源?
非常感谢您的建议!
【问题讨论】:
-
“它可能使用了一个已经在使用的线程”。不,它可能不会,因为线程正在使用中。它可能会使用一个空闲线程,它会像往常一样尝试获取锁。你的
finally子句是错误的,你不应该检查isLocked()。lock()在try的开头,而unlock()在finally子句中。没有如果。 -
好的,谢谢。所以我的理解是不正确的:)所以我对锁定没有问题。
-
好吧,你可以(使用
isLocked()),所以你可能想通过这个docs.oracle.com/javase/tutorial/essential/concurrency/… -
我会看看 - 谢谢,W.R.T
isLocked(),我在lock()之前有一个包含一些东西的方法,根据结果,这可能意味着lock()未被调用。发生这种情况时,调用unlock()会抛出 Illegal异常。我本可以使用嵌套的 try-finally 块,但这变得很乱 -
isLocked()的通常(尽管非常罕见)用例是tryLock()。更好的是使用更高级别的并发原语,这样您就不需要处理原始锁。我推荐阅读并发教程。
标签: java multithreading locking completable-future reentrantlock