shared mutable state
If you remember one phrase from this entire field, make it this one. Almost every concurrency bug traces back to shared mutable state - data that more than one thread can reach (shared) and that can change (mutable). Picture a shared whiteboard that several people are allowed to erase and rewrite at the same time, with no rule about taking turns. Sooner or later two of them write over each other and the board ends up wrong. That whiteboard is shared mutable state, and the chaos is exactly what concurrent threads can do to shared memory.
Break the phrase into its two words, because each is necessary and removing either makes the danger go away. Shared means more than one thread can see the same data - a global variable, a static, or any heap object reachable through a pointer that two threads hold. Mutable means the data can change - at least one thread writes to it. Put them together and you have a place where one thread's write can collide with another thread's read or write, with the operating system free to interleave them in any order. If data is shared but never changes (immutable), there is no conflict - everyone just reads. If data is mutable but not shared (a thread's own stack local), there is no conflict either - only one thread touches it. The hazard needs both at once.
Why this framing is so useful: it turns 'concurrency is scary' into a concrete checklist. To find your concurrency bugs, find the shared mutable state, then ask how it is protected. The standard cures all attack one leg of the tripod: make the data not shared (give each thread its own copy, or use thread-local storage), make it not mutable (only read it after it is built), or - when you truly need shared writable data - guard every access with synchronization (a mutex, an atomic) so threads take turns instead of colliding. Field n is largely a catalogue of that last option.
A global int requests = 0; that many request-handling threads each do requests++ on is shared mutable state. Give each thread its own local counter and add them up at the end, and the hazard vanishes - the data is no longer shared while being mutated.
The bug needs both legs: shared AND mutable. Remove either and the conflict is gone.
Sharing read-only data is safe; the danger is sharing data that is being written. A common surprise: a const pointer or a 'getter' can still expose mutable shared state if the underlying object changes - it is the data's mutability, not the access style, that matters.