Is concurrency 'hard'?
Is concurrency hard? You can certainly find people arguing both sides of the issue if you look hard enough (mostly for rather than against). But I think the question may be blurring two things together that are worthwhile to separate: 'concurrency is difficult to learn and understand', and 'concurrency is tricky to implement correctly'.
My experience differs from Joel's in another respect: Recursion is mildly hard. Closures and continuations are hard. Concurrency is very hard. I never found pointers hard at all.
For me, it really depends on what sense of 'hard' one means. Closures and continuations are somewhat mind-bending to understand, but I've found closures easy to use in programs. In a way closures are so easy to use that you may not realize that you are using them, because things 'just work'. (I haven't really used a language with full continuation support, but Python's limited version is pretty easy to use too.)
(Arguably closures (and Python's limited continuations) are only hard to understand if you insist on knowing how the magic works.)
So is concurrency hard? My belief is that concurrency isn't particularly difficult to understand, but it is definitely difficult to use. I think we have lots and lots of experimental evidence of the latter, and implicit experimental evidence of the former in that there are lots of people who think they understand concurrency well enough to use it.
It's possible that concurrency is harder to understand than I remember. I suspect that knowing something about computer architecture and assembly language helped me when I was learning about it, so I don't know how it would hit people without that sort of a background. (Although I think any graduate of a good CS undergrad program ought to have been exposed to both.)