Card 04/ 09

ExampleDifficulty: Advanced1 min

Race Condition Example: Moving Money Between Two Accounts

Four lines, no increment operator, and a lost update all the same.

A race condition does not need a counter. Any code that reads a shared value, decides or computes from it, and writes back can lose an update, and a transfer between two accounts does all three.

java
void transfer(Account from, Account to, long pence) {
    if (from.balance >= pence) {
        from.balance -= pence;
        to.balance   += pence;
    }
}

Two threads each transfer a hundred pounds out of an account holding a hundred and fifty. Both read the balance on line 2, both find it sufficient, and both carry on. The account can end at minus fifty, with two hundred pounds paid out of a hundred and fifty; and because -= is itself a read and a write, the balance can also end wrong in other ways, depending on timing.

Put this beside two threads each running count++ on a shared field and losing increments, and the shared structure comes out: both read a value, computed from it, and wrote it back, and another thread got in between the read and the write. One is three characters and one is four lines; the mechanism is identical.

The same shape in three different places
WrittenReadsThen writes
count++The countThe count plus one
balance -= penceThe balanceThe balance less the amount
if (m.get(k) == null) m.put(k, v)Whether the key is thereThe key, if it was not

The check on line 2 belongs to the same operation as the write on line 3, and nothing in the language says so. Recognising that shape is most of what it takes to spot a race before it ships.