I've been doing concurrent programming in Ada for nearly 20 years now.
The language itself (not some tacked on library) supports threading ("tasks"), multiple scheduling models, and multiple synchronization paradigms. You can even build your own synchronization schemes using the built in primitives.
You can think of Ada's rendezvous as sort of a procedural-oriented synchronization facility, while protected objects are more object-oriented. Rendezvous are similar to the old CS-concept of monitors, but much more powerful. Protected objects are special types with synchronization primitives that allow you to build things exactly like OS locks, semaphores, events, etc. However, it is powerful enough that you can also invent and create your own kinds of sync objects, depending on your exact needs.