Capture multiple castles atomic

Suppose we have to make a transfer between any two accounts (among hackers) as part of a transaction.
And there would be several similar transactions running concurrently in a typical multi-threaded environment.
A typical convention would be as follows (maintaining the blocking order according to a pre-negotiated convention):

lock account A
lock account B
transfer(A,B)
release B
release A

      

Is there a way to try to block and free as an atomic operation?

+3


source to share


3 answers


Yes, there is: you need to lock the locks under a padlock. In other words, you need to create a locking hierarchy. But this solution is not very efficient as it reduces the granularity of the lock.



It seems that in your case it will be enough to always make locks in the same order. For example, first block the user with the lower ID first.

+4


source


A transaction is atomic by ACID definition (A is for atomicity). Isolation (at least READ_COMMITED

one) ensures that another transaction, which can happen for account A at the same time, will wait until the previous started transaction is completed. So you don't really need to lock them explicitly, as they will be locked by an internal implementation (like the database) and that locks will be more efficient as they can use optimistic locking methods.

But this is only true if they all participate in the same transactional context (for example, in the environment JTA

). In such an environment, you can simply start a transaction at the beginning of the transfer method and do not need to lock account A and account B.



In case they are not in the same transactional context, you can introduce another lock object, but this will significantly slow down performance, since the threads will be blocked even if one is working with accounts A and B, and the other with accounts C and D. There are ways to prevent this situation (see, ConcurentHashMap

for example, where the locks are on buckets and not on the whole object).

But with your specific example of an answer, there may only be some general thoughts, as the example should be short to explore more. I think the option to lock account A and account B in a specific order (must be very careful with this - as it could lead to potential deadlocks. And assuming there is more than just a transfer method that could work with them but also very risky) is normal for this situation.

0


source


You can try using the following code. Note: this only works for two locks and I'm not sure how to make it scalable for more locks. The idea is that you take the first lock and try to take the second one. If this fails, we know that one lock is currently free and the other is busy. So we release the first lock, and you invert them, so we will lock the one that was busy and try to take the one that was (WAS!) Free if it is still free. Rinse and repeat. It is statistically impossible for this code to end up on StackOverflow, I think handling it and throwing an error is better than making it loop, as that would signal that something is going really badly somewhere.

public static void takeBoth(ReentrantLock l1,ReentrantLock l2) {
    l1.lock();
    if(l2.tryLock()) {return;}
    l1.unlock();
    try{takeBoth(l2,l1);}
    catch(StackOverflowError e) {throw new Error("??");}
  }
  public static void releaseBoth(ReentrantLock l1,ReentrantLock l2){
    if(!l1.isHeldByCurrentThread()) {l1.unlock();}//this will fail: IllegarMonitorState exception
    l2.unlock();//this may fail, in that case we did not touch l1.
    l1.unlock();    
    }

      

0


source







All Articles