Using a mutex to protect critical data is sometimes not enough. Suppose you are in a critical section, but you need to wait for some particular resource. If your thread goes to sleep waiting for this resource, it is possible that no other thread will be able to release the resource because it cannot enter the critical section---the original process still has it locked. You need to be able to give up temporarily your exclusive use of the critical region and simultaneously tell people that you're waiting for a resource. When the resource becomes available, you need to be able to grab it and reobtain the lock on the critical region, all in one step.
This is where condition variables come in. A condition variable is simply a semaphore that is associated with a resource and is used within the protection of a particular mutex. When you need a resource that's unavailable, you wait on a condition variable. That action releases the lock on the corresponding mutex. When some other thread signals that the resource is available, the original thread comes off the wait and simultaneously regains the lock on the critical region.