Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CompletableFuture and locks

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?

like image 930
Isen Ng Avatar asked Nov 01 '25 01:11

Isen Ng


1 Answers

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!

like image 86
Michael Peyper Avatar answered Nov 03 '25 13:11

Michael Peyper



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!