You only have to do it yourself once you decide to use threads.
And there is not really a way around that. If the language mandated that all primitive operations (for whatever reasonable operation of 'primitive') were atomic, most multi-threaded programs would slow down to a crawl.
Even ignoring that, having atomic primitives is not sufficient to prevent race conditions. Things like "if balance > withdrawal {balance -= withdrawal; ...} need larger ranges of locking, and no compiler is going to tell you how large they have to be.
And there is not really a way around that. If the language mandated that all primitive operations (for whatever reasonable operation of 'primitive') were atomic, most multi-threaded programs would slow down to a crawl.
Even ignoring that, having atomic primitives is not sufficient to prevent race conditions. Things like "if balance > withdrawal {balance -= withdrawal; ...} need larger ranges of locking, and no compiler is going to tell you how large they have to be.