【发布时间】:2017-05-30 13:30:39
【问题描述】:
我有原同步码,
Lock lock = getLock(userId); // get a lock from guava Striped locks
lock.lock();
try {
// do some database writing action
updateUserByUserId(userId);
return updateUserPropertiesByUserId(userId);
}
finally {
lock.unlock();
}
锁定的目的是在更高级别模拟悲观数据库锁定。
在 Java8 中,引入了 CompleteableFutures。 updateUserByUserId(userId); 和 updateUserPropertiesByUserId(userId); 现在可以返回 CompletableFuture<Void> 以实现完全异步。
我的问题是,我怎样才能使用相同的机制?还是这种锁定方式完全错误? (我真的不想依赖数据库的锁定。如果可能的话,我想在应用层而不是数据库层处理这个)
我试过了
Lock lock = getLock(userId); // get a lock from guava Striped locks
return CompletableFuture
.supplyAsync(() -> {
lock.lock();
})
.thenCompose(VOID -> updateUserByUserId(userId))
.thenCompose(entity -> updateUserPropertiesByUserId(userId))
.whenComplete((entity, ex) -> {
lock.unlock();
});
但我在lock.unlock(); 上收到了IllegalMonitorStateException,这是意料之中的,因为您不应该解锁不同线程中的锁。
有什么建议吗?
【问题讨论】:
-
你的锁在模仿悲观锁,而不是乐观。不过,我不明白为什么您不依赖数据库事务和锁定。如果您的应用程序有多个实例正在运行,或者其他人正在访问同一个数据库,那么您的锁定机制将不起作用。
-
您对锁的说法是乐观的。我把它改成悲观了。这是一个简单的示例来演示我正在尝试做的事情。实际上,我的数据库只有 1 个访问器,这是我的微服务,只有 1 个接口。在主动-主动设置中,将使用从缓存层检索到的锁。为什么我要这样做是因为我想“快速失败,尽早失败”,而不是再跳到数据库,这会引发错误,让应用层将数据库错误转换为异常,然后捕获它例外。这是一个相当大的打击。
-
并且 DB 错误转换为 Java 异常可能因 DB 不同而异……甚至同一个 DB 的不同风格。如果可能的话,我真的不想处理。
-
把这个线性操作分成四个阶段是没有意义的,它仍然是一系列直接依赖的操作,你只会让它变得更复杂和容易出错,没有任何好处。这两个更新方法仅在单独调用时是异步的,这将违反您的锁定要求。在您的
CompletableFuture链中,您正在等待每个操作完成,然后根据需要启动下一个操作,这使得整个序列像以前一样线性。 -
哦,有道理!让我重温一下
标签: java multithreading locking guava completable-future