the memory consistency model
A memory consistency model is the contract that says, when multiple threads read and write shared memory, what orderings of those reads and writes a thread is allowed to observe. It sounds like it should be obvious — surely writes happen in the order you wrote them and everyone sees them that way. The unsettling truth is that on real hardware and after real compilers optimize, that is often not so. For speed, CPUs and compilers reorder, buffer, and cache memory operations, so one thread can see another thread's writes in a different order than they were issued. The memory model is the precise, language-or-hardware-defined rulebook for what you can and cannot count on.
The simplest, most intuitive model is sequential consistency: it pretends all the threads' operations were merged into one global sequence, each thread's own operations stay in program order, and every thread sees that same single order. This is what most people imagine concurrency is. The trouble is that enforcing it everywhere is expensive, so real systems use relaxed (weaker) memory models that allow more reordering for performance. A famous symptom: two threads, x and y both start at 0; thread 1 sets x = 1 then reads y, thread 2 sets y = 1 then reads x. Under sequential consistency at least one of them must read a 1. Under a relaxed model both can read 0, because each thread's write can be buffered and not yet visible to the other when the read happens — a result that looks impossible but is real.
What you do about it is insert memory barriers (fences) or use atomic operations with explicit ordering, which tell the hardware and compiler 'do not reorder across this point' and 'make this write visible before that read.' Higher-level tools — locks, condition variables, and properly-used atomics — bundle the right barriers for you, which is the main reason most programmers can ignore the gory details: if you protect all shared access with locks, the language guarantees you sequentially-consistent-looking behavior. The honest, important caveat is that the moment you write lock-free code, roll your own synchronization, or share data without locks, the relaxed memory model is suddenly your problem, and reasoning about it correctly is one of the hardest things in all of programming.
x = y = 0. Thread 1: x = 1; r1 = y. Thread 2: y = 1; r2 = x. Under sequential consistency you can never get r1 == 0 and r2 == 0. Under a relaxed model you can, because each store may be buffered and not yet visible when the other thread loads.
The 'store buffering' litmus test — the textbook example of a result that a relaxed memory model permits but our intuition forbids.
If you guard every shared access with the same lock, you can think in terms of sequential consistency and ignore the relaxed model entirely — the lock inserts the necessary barriers. The relaxed model only bites you when you synchronize by hand.