I have the original synchronous code,
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();
}
The purpose of the lock is to mimic pessimistic database locking at a higher level.
In Java8, CompleteableFutures are introduced. And updateUserByUserId(userId); and updateUserPropertiesByUserId(userId); can now return CompletableFuture<Void> for a fully async implementation.
My question is, how can I employ the same mechanism? or is this way of locking completely wrong? (I don't really want to rely on the database's locking. If possible, I want to handle this in the app layer instead of the database layer)
I have tried
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();
});
But I've been getting IllegalMonitorStateException on lock.unlock();, which is expected, because you're not supposed to unlock a lock in a different thread.
Any suggestions?
You can supply an executor to the ...Async variants of the callbacks, so supplying the same single threaded executor to the one callbacks that lock and unlock should ensure they execute on the same thread.
Lock lock = getLock(userId); // get a lock from guava Striped locks
Executor lockExecutor = Executors.newSingleThreadExecutor();
return CompletableFuture
.supplyAsync(() -> {
lock.lock();
}, lockExecutor)
.thenCompose(VOID -> updateUserByUserId(userId))
.thenCompose(entity -> updateUserPropertiesByUserId(userId))
.whenCompleteAsync((entity, ex) -> {
lock.unlock();
}, lockExecutor);
NOTE: I haven't run this, hopefully it works!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With