a concurrency invariant
An invariant is a statement about your data that is always supposed to be true — a promise the program keeps. For a bank, an invariant might be 'the sum of all account balances equals the total money in the bank.' For a bounded buffer, it might be '0 ≤ count ≤ N' and 'count equals the number of items actually stored.' In single-threaded code these promises are easy to honor: you might temporarily break an invariant in the middle of an operation (debit one account before crediting the other, so for a moment money seems to vanish), but nobody else is looking, and the invariant is restored by the time you finish. A concurrency invariant is the same idea taken seriously when other threads are watching.
The whole danger of concurrency is precisely that other threads can look in the brief window while an invariant is broken. A money transfer that subtracts from account A and then adds to account B passes through a state where the total is wrong; if another thread reads both balances right then, it sees impossible data. The cure is to make the critical section — the span of code where the invariant is temporarily violated — atomic with respect to everyone else, usually by holding a lock across it. The discipline is: hold the invariant true at every point where you release the lock, and only break it while you hold the lock. Then no other thread can ever observe the broken state.
Designing with invariants is the single most useful way to reason about concurrent code, far more reliable than tracing every possible interleaving in your head (there are exponentially many). You write down what must always hold, you identify exactly which lines transiently break it, and you make sure those lines run while the data is locked. A correct lock-based design is just: every thread that touches the shared data uses the same lock, and the invariant is re-established before unlocking. It also clarifies the goal of minimizing the critical section — make the locked region as small as possible (do slow or unrelated work outside it) while still keeping it large enough to cover every moment the invariant is broken.
Invariant for a transfer: total = balance[A] + balance[B] is constant. The two updates 'balance[A] -= amount' and 'balance[B] += amount' must both run under one lock, because total is momentarily wrong in between.
Break the invariant only while holding the lock; restore it before unlocking. Then no other thread can ever see the inconsistent state.
An invariant is a design tool, not something the language enforces — the compiler won't warn you if you read shared data without the lock. Correctness depends on every single access following the discipline; one careless unprotected read can observe the broken state.