Modern programming languages like Java or C++ have standard library API’s that make it relatively easy to take advantage of multicore processors by enabling simultaneous execution of multiple tasks within a program without the programmer having to think too deeply about how the processors work. For example std::thread in C++11 and later, std::jthread in C++20 and later, and the java.lang.Thread class in Java since JDK1.0.

However, if you want to understand how CPU’s work more deeply (and a bit about how modern compilers take advantage of them), this YouTube video, What Every Programmer Should Know about How CPUs Work • Matt Godbolt • GOTO 2024, is a great place to start. That talk mainly focuses on what happens in a single core, this five minute YouTube video, CPU Cores VS Threads Explained, talks a bit about how multiple threads can be spread across multiple cores.

Of course, for many applications (those that are I/O bound, like some web servers, for example), concurrency is enough to be useful even without the parallelism potentially provided by multiple cores (CPU bound applications, like some image processing applications, typically benefit more from multicore processors). A useful description of concurrency and parallelism in this context can be found in this Stack Overflow answer to the question “What is the difference between concurrency and parallelism?”

Concurrency is when two or more tasks can start, run, and complete in overlapping time periods. It doesn’t necessarily mean they’ll ever both be running at the same instant. For example, multitasking on a single-core machine.

Parallelism is when tasks literally run at the same time, e.g., on a multicore processor.