While concurrency improves performance and resource utilization, it also makes your code much harder to design and debug. This is because, unlike in a single-threaded flow, the timing of operations cannot be determined upfront. In single-threaded code, you either write to the resource or read from it, but you always know the order of the operations and can, therefore, predict the state of the object.
With concurrency, several threads or processes can be either reading from an object or modifying it at the same time. If the modifications aren't atomic, we can reach one of the variants of the common update problem. Consider the following code:
TransactionStatus chargeTheAccount(AccountNumber acountNumber, Amount amount) { Amount accountBalance = getAcountBalance(accountNumber); if (accountBalance > amount) { setAccountBalance(accountNumber, accountBalance - amount); return TransactionStatus::TransactionSuccessful; ...