Java synchronisation: atomically moving money across account pairs?

Images
Related searches

How to make moving money from one account to another atomic? For:

public class Account {
    public Account(BigDecimal initialAmount) {...}
    public BigDecimal getAmount() {...}
    public void setAmount(BigDecimal amount) {...}
}

I expect that pseudo-code:

public boolean transfer(Account from, Account to, BigDecimal amount) {
    BigDecimal fromValue = from.getAmount();
    if (amount.compareTo(fromValue) < 0)
         return false;
    BigDecimal toValue = to.getAmount();
    from.setAmount(fromValue.add(amount.negate()));
    to.setAmount(toValue.add(amount));
    return true;
}

update accounts safely in multithreading environment, I see danger case as:

acc1 --> acc2  ||  acc2 --> acc1
acc1 --> acc2  ||  acc2 --> acc3  ||  acc3 --> acc1
...

Easiest solution is to make blocking on shared object, but it will be inefficient for cases like:

acc1 --> acc2  ||  acc3 --> acc4  and  acc1 != acc3 and acc2 != acc4

I expect that independent moves are performed in parallel.

UPDATE Seems that suggested solution:

synchronize (acc1) {
   synchronize (acc2) {
     ....
   }
}

lead to deadlock as 2 locks acquired sequentially...

UPDATE 2 what do you mean with "update accounts safely in multithreading environment" exactly? Is the only worry that the accounts won't end up having minus funds or is there some other problem?

If acc1(2); acc2(3) and acc1 --1--> acc2 and acc2 --2--> acc1 I expect consistency: (acc1, acc2) has (3, 2) value, but not (4, 2) or (3, 4). Total should be 5, and not 1+3=4 or 4+3=7.

how much concurrent transaction are you expecting at a time ? 1000-10000 - so lock on shared object isn't efficient.

A simple solution could be to use a lock per account, but to avoid deadlock you have to acquire locks in the same order always. So, you could have a final account ID, and acquire the lock of the account with a less id first:

public void transfer(Account acc1, Account acc2, BigDecimal value) {
    Object lock1 = acc1.ID < acc2.ID ? acc1.LOCK : acc2.LOCK;
    Object lock2 = acc1.ID < acc2.ID ? acc2.LOCK : acc1.LOCK;
    synchronized (lock1) {
       synchronized (lock2) {
          acc1.widrawal(value);
          acc2.send(value);
       }
    }
}

Reading 20, Part 1: Synchronization, How to make moving money from one account to another atomic? For:public class Account { public Account(BigDecimal initialAmount) {. Java synchronisation: atomically moving money across account pairs? Tags ajax android angular api button c++ class database date dynamic exception file function html http image input java javascript jquery json laravel list mysql object oop ph php phplaravel phpmysql phpphp post python sed select spring sql string text time url view windows

One way to do this is to have a transaction log. Before moving the money, you'll need to write to the transaction log of each account what you intend to do. The log should contain: the amount of money that's taken in/out of the account, and an lock which is shared between the log pair.

Initially the lock should be in a blocked state. You created the log pair, one with amount of X and the other with amount of -X, and both shares a lock. Then deliver the log entry to the inbox of the respective accounts, the account from which money is taken out should reserve that amount. Once you've confirmed that they're delivered safely, then release the lock. The moment the lock is released you're at a point if no return. The accounts then should resolve themselves.

If either of the party want to fail the transaction at any time before the lock is released, then simply remove the logs and return the reserved amount to the main balance.

This approach may be a bit heavy, but it would also work in a distributed scenario where the accounts actually are in different machines, and the inboxes actually would have to be persisted, to ensure money never get lost if any of the machine crashes/goes offline unexpectedly. Its general technique is called two phase locking.

How to Synchronize Blocks by the Value of the Object in Java, A transfer between accounts needs to lock both accounts, so that money can't Let's see how to use synchronization to implement a threadsafe ADT. Locks are so commonly-used that Java provides them as a built-in language feature. those accesses will be guaranteed to be atomic — uninterrupted by other threads . 48 Java synchronisation: atomically moving money across account pairs? Mar 26 '15 33 How to disable or bypass Hardware Graphics Acceleration(Prism) in JavaFX Sep 12 '13

I would propose to create a method Account.withdraw(amount) which throws an exception if it doesn't have sufficient funds. This method needs to be synchronized on the account itself.

Edit:

There also needs to be a Account.deposit(amount) method which is synchronized on the receiving account instance.

Basically this will result in a lock of the first account while withdrawing and then another lock on the receiving account while depositing. So two locks but not at the same time.

Code sample: Assumes that withdraw/deposit are synchronized and return boolean success status rather than throw exception.

public boolean transfer(Account from, Account to, BigDecimal amount) {
    boolean success = false;
    boolean withdrawn = false;
    try {
        if (from.withdraw(amount)) {
            withdrawn = true;
            if (to.deposit(amount)) {
                success = true;
            }
        }
    } finally {
        if (withdrawn && !success) {
            from.deposit(amount);
        }
    }

    return success;
}

LCK07-J. Avoid deadlock by requesting and releasing , We explore the ways we can synchronize blocks of code using the The transfer of money in the payment service depends on the other two an internal database and stores data related to accounts of its users. In this service, we can use a JPA transaction to make some actions as atomic operations in the� 5 Java synchronisation: atomically moving money across account pairs? Apr 1 '15 3 How can I replace two strings in a way that one does not end up replacing the other?

You can create an extra Account T that exists solely for transferring the money. So if you want to transfer from A to B you actually transfer from A to T and then from T to B. For each of these transfers you only lock either A or B depending on which account is participating in the transfer. Since you are using the same type for transfers, you end up with little extra code and therefore low maintenance costs.

To reduce the number of extra accounts you could hold them in a pool. If you have a thread pool that is processing transfers, than you can assign each thread it's own extra account. Therefore you don't need to request and release those extra accounts from/to a pool too often.

org.jboss.cdi.tck.tests.extensions.interceptionFactory.customBean , To avoid data corruption in multithreaded Java programs, shared data must be that atomically transfers a specified amount from one account to another. would happen if they both transfer money from one account to a second account) or if This compliant solution avoids deadlock by synchronizing on a private static� 8 File changed listener in Java Mar 17 '13 8 Why do I often see this Javascript construct/convention? (i.e. creating 'var foo;' on it's own line, instead of via assignment) Jun 11 '13

One approach is to use kind of "striped lock" with lock/unlock methods operating on several locks. Accounts are mapped to locks using hashCode, the more locks you allocate, the more parallelism you get.

Here's code sample:

public class StripedLock {

    private final NumberedLock[] locks;

    private static class NumberedLock {
        private final int id;
        private final ReentrantLock lock;

        public NumberedLock(int id) {
            this.id = id;
            this.lock = new ReentrantLock();
        }
    }


    /**
     * Default ctor, creates 16 locks
     */
    public StripedLock() {
        this(4);
    }

    /**
     * Creates array of locks, size of array may be any from set {2, 4, 8, 16, 32, 64}
     * @param storagePower size of array will be equal to <code>Math.pow(2, storagePower)</code>
     */
    public StripedLock(int storagePower) {
        if (!(storagePower >= 1 && storagePower <= 6)) { throw new IllegalArgumentException("storage power must be in [1..6]"); }

        int lockSize = (int) Math.pow(2, storagePower);
        locks = new NumberedLock[lockSize];
        for (int i = 0; i < locks.length; i++)
            locks[i] = new NumberedLock(i);
    }

    /**
     * Map function between integer and lock from locks array
     * @param id argument
     * @return lock which is result of function
     */
    private NumberedLock getLock(int id) {
        return locks[id & (locks.length - 1)];
    }

    private static final Comparator<? super NumberedLock> CONSISTENT_COMPARATOR = new Comparator<NumberedLock>() {
        @Override
        public int compare(NumberedLock o1, NumberedLock o2) {
            return o1.id - o2.id;
        }
    };


    public void lockIds(@Nonnull int[] ids) {
        Preconditions.checkNotNull(ids);
        NumberedLock[] neededLocks = getOrderedLocks(ids);
        for (NumberedLock nl : neededLocks)
            nl.lock.lock();
    }

    public void unlockIds(@Nonnull int[] ids) {
        Preconditions.checkNotNull(ids);
        NumberedLock[] neededLocks = getOrderedLocks(ids);
        for (NumberedLock nl : neededLocks)
            nl.lock.unlock();
    }

    private NumberedLock[] getOrderedLocks(int[] ids) {
        NumberedLock[] neededLocks = new NumberedLock[ids.length];
        for (int i = 0; i < ids.length; i++) {
            neededLocks[i] = getLock(i);
        }
        Arrays.sort(neededLocks, CONSISTENT_COMPARATOR);
        return neededLocks;
    }
}

    // ...
    public void transfer(StripedLock lock, Account from, Account to) {
        int[] accountIds = new int[]{from.getId(), to.getId()};
        lock.lockIds(accountIds);
        try {
            // profit!
        } finally {
            lock.unlockIds(accountIds);
        }
    }

Java synchronisation: atomically moving money across account pairs? public boolean transfer(Account from, Account to, BigDecimal amount) { boolean success� 4 determine whether process has done execution in Java Jun 12 '15 4 List of columns in sql query Aug 10 '15 4 Getting data from a given String separated by (,,-) in java Oct 13 '15

On a single-CPU system, a CAS typically takes on the order of a handful of clock cycles, since no synchronization across processors is necessary. As of this writing, the cost of an uncontended CAS on multiple CPU systems ranges from about ten to about 150 cycles; CAS performance is a rapidly moving target and varies not only across

Mutual exclusion. As a simple example, consider a thread-safe object for performing transactions on a bank account: monitor class Account { private int balance := 0 invariant balance >= 0 public method boolean withdraw(int amount) precondition amount >= 0 { if balance < amount { return false} else { balance := balance - amount return true} } public method deposit(int amount) precondition

The data sync would be much better and faster, if it can be done on the basis of some kind of delta identifier or flag. Basically, you should update the target db data rows only when it is out of sync with the source db. In SQL server db, you can take the help of the Checksum fn also to build the delta based identifier.

Comments
  • Lock both of the accounts. But beware of deadlocks.
  • How to avoid deadlock? I don't know a way to block atomically on 2 object simultaneously.
  • One way of doing it is by acquiring each lock in the same order across each threads. If you're accounts have unique IDs, then that's not too hard.
  • why not make transfer method static synchronized?
  • Give each account a Lock object. Order the accounts by name/id. lock each Lock object and transfer the money. unlock in a finally. Done.
  • Having lock object inside an account (or, the same, locking on account object itself) means one need to ensure that only one instance of particular account exists inside JVM.
  • Instead of using a LOCK field, you can just use the account object itself as the lock.
  • @tcooc - That would still require having only one copy of an account object.
  • As a more general solution, you can use as many and as granular locks as you like and never worry about deadlock or livelock as long as you always acquire any and all locks in the same order.
  • I wonder if the deadlock could be avoided if Account object's debit and credit methods are synchronized? then would be run into race conditions?
  • Are there exist any transaction log Java libraries?
  • This looks like really really awesome introduction to log-based persistence and it contains quite a few links to implementations: engineering.linkedin.com/distributed-systems/…
  • what about two object of same account ?