queue must be protected by a unique mutex object, m. The "producer" threads will want to wait on a monitor using lock m and a condition variable which blocks until the queue is non-full. The "consumer" threads will want to wait on a different monitor using the same mutex m but a different condition variable which blocks until the queue is non-empty. It would (usually) never make sense to have different mutexes for the same condition variable, but this classic example shows why it often certainly makes sense to have multiple condition variables using the same mutex. A mutex used by one or more condition variables (one or more monitors) may also be shared with code that does not use condition variables (and which simply acquires/releases it without any wait/signal operations), if those critical sections do not happen to require waiting for a certain condition on the concurrent data. Monitor usage The proper basic usage of a monitor is: acquire(m); // Acquire this monitor's lock. while (!p) { // While the condition/predicate/assertion that we are waiting for is not true... wait(m, cv); // Wait on this monitor's lock and condition variable. } // ... Critical section of code goes here ... signal(cv2); // Or: broadcast(cv2); // cv2 might be the same as cv or different. release(m); // Release this monitor's lock. To be more precise, this is the same pseudocode but with more verbose comments to better explain what is going on: // ... (previous code) // About to enter the monitor. // Acquire the advisory mutex (lock) associated with the concurrent // data that is shared between threads, // to ensure that no two threads can be preemptively interleaved or // run simultaneously on different cores while executing in critical // sections that read or write this same concurrent data. If another // thread is holding this mutex, then this thread will be put to sleep // (blocked) and placed on m's sleep queue. (Mutex "m" shall not be // a spin-lock.) acquire(m); // Now, we are holding the lock and can check the condition for the // first time. // The first time we execute the while loop condition after the above // "acquire", we are asking, "Does the condition/predicate/assertion // we are waiting for happen to already be true?" while (!p()) // "p" is any expression (e.g. variable or // function-call) that checks the condition and //