I think providing a lock is like giving up control to someone else. It’s inherent coupling. To contrast, when I first learned about them I thought of them as a protection mechanism of an isolated part, but they are an execution contract that everyone participates in.
This matches well with one of the beliefs I've been polishing up over the past few years which I don't think is well understood, which is that structured programming tends to create more coupling than we realize in deep call stacks. When you end up with a 2000-line call stack in Java, that's 2000 function invocations coupled together by the way structured programming works; for instance, at a bare minimum, that's 1999 functions that can't progress until the innermost one does, and that's not the only form of coupling.
I think the coupling induced by structured programming is fairly light per stack frame on average, but they tend to add up quickly because they're just so darned easy to add more of.
Locks are another thing that makes this really come to light. The more stack frames between some function and something above it in the stack that has taken a lock, the more likely it is for execution to eventually wander back into something trying to take a lock inadvisably.
I mean, I've had a number of deadlocks just within an object where it has some internal lock, and a method takes that lock, then tries to call a method that takes the same lock. Whoops. Easy to fix, of course, but that's the easy case. The cases get harder in real code.
I just think we underestimate the coupling it introduces. Doesn't mean the solution is to throw it all out. I'm not even sure there is "a solution". Just something to keep in mind.
I know about the theoretical solution of taking locks always in a defined order, but I don't consider it a practical solution. If taking locks willy-nilly lets you scale to program size X, this might give you 2X or 3X, but that's not nearly large enough in practice.
& dandelany, the community has chipped in and answered for me. I endorse this subthread, at least, as I write this comment. :)
I've got multi threaded C++ in production right now, and it is because the code is small and avoids multiple locks that I have any faith in it. As a code base expands, in kloc and in time, this gets harder and harder to reason about.
danluu claims there's other reasons it's not good enough but I was never able to figure out what he meant. Exception safety maybe.