Two threads increment the same counter. count++ looks like one operation. It is three: read, add, write. Both read 41. Both write 42. One increment vanished.
A lock solves it by taking turns. Threads queue, one proceeds, the rest block and get scheduled later. Simple, reliable, and everyone else is asleep while they wait.
Compare-and-swap asks the hardware instead: if this value is still 41, make it 42, otherwise tell me what it actually is. One atomic instruction, no blocking, retry on collision. Under light contention it is very fast. Under heavy contention threads spin and burn CPU achieving nothing.
So the choice follows the traffic. Low contention favours CAS. High contention favours a lock, which wastes less. Updates spanning several variables favour a lock, which is simply easier to get right.
AtomicInteger, Interlocked and every lock-free queue sit on top of the same instruction.
Some conflicts are best avoided by taking turns. Others by acting, checking, and being willing to start over.
