Once you start thinking about a program as a sequence of loads/stores (i.e. reads/writes to shared memory) and note that Pipelining/OOO/Superscalar are techniques to parallelize these (and other) instructions for a single thread of control you start getting an idea of how sequential order can be preserved though the actual execution is not quite so. Now add in another thread of control on another core and you get the idea of Memory Consistency problems and can be introduced to various "Consistency Models" (https://en.wikipedia.org/wiki/Consistency_model). Next introduce caches in between and you get "Cache Coherence" problems (https://en.wikipedia.org/wiki/Cache_coherence) Walk through a simple example like "c=a+b" in thread1 and "z=x+c" in thread2 and observe the interleavings and possible problems. Now move on to "Atomic Instructions" (https://en.wikipedia.org/wiki/Linearizability#Primitive_atom...) and how they can be used to implement "Locking" (https://en.wikipedia.org/wiki/Lock_(computer_science) ). Finally show "Memory Barriers" (https://en.wikipedia.org/wiki/Memory_barrier) as a means of enforcing a checkpoint on memory operations. All these together should make an understanding of Concurrency clearer. One can then jump back up into the language and study the language primitives for concurrency management and their common usage patterns.
A formal education, in any domain, forces you to explore areas of knowledge that may not be directly applicable to immediate 'real world' problems. It does however give you the background to identify and explore those areas when you approach something that does overlap with work that people have spent literal lifetimes considering ahead of you.
I say this as someone without a formal CS education but has been working with computers for some time. I've spent countless hours, days, and months playing catch-up on that knowledge when I stumble into those blindspots.