На самом низком уровне синхронизация делается при помощи атомарных инструкций процессора. Скажем, модно использовать атомарную операцию обмена, чтобы записать в ячейку 1. Если предыдущее значение было 0, то мы теперь владеем каким-либо ресурсом, если там 1, то его уже занял кто-то другой и нужно попробовать снова позже.
На более высоком уровне обычно в дело вступает ОС. Так как процессы изолированы друг от друга и не могут взаимодействовать напрямую, необходимо посредничество операционной системы. Как это сделано, зависит от системы. Выберите конкретную и гуглите:
Вопрос на StackOverdlow
linux.die.net/man/2/futexTo reiterate, bare futexes are not intended as an easy-to-use abstraction for end-users. Implementors are expected to be assembly literate and to have read the sources of the futex user-space library referenced below.
Исходники pthread (Который используется для реализации std::tread, std::mutex, etc)
EDIT: И не используйте lock/unlock напрямую. Оберните их в lock_guard.