You might get an experience from studying all semaphore examples that signal and wait calls may scatter everywhere in your program in a not-so-well structured way. If you really get such a feeling, the concept of monitor comes to rescue. The concept of monitor came from C. A. R. Hoare's 1974 paper.
A monitor has four components as shown below: initialization, private data, monitor procedures, and monitor entry queue. The initialization component contains the code that is used exactly once when the monitor is created, The private data section contains all private data, including private procedures, that can only be used within the monitor. Thus, these private items are not visible from outside of the monitor. The monitor procedures are procedures that can be called from outside of the monitor. The monitor entry queue contains all threads that called monitor procedures but have not been granted permissions. We shall return to this soon.
Therefore, a monitor looks like a class with the initialization, private data and monitor procedures corresponding to constructors, private data and methods of that class. The only major difference is that classes do not have entry queues.
Monitors are supposed to be used in a multithreaded or multiprocess environment in which multiple threads/processes may call the monitor procedures at the same time asking for service. Thus, a monitor guarantees that at any moment at most one thread can be executing in a monitor! What does this mean? When a thread calls a monitor procedure, we can view the called monitor procedure as an extension to the calling thread. If the called monitor procedure is in execution, we will say the calling thread is in the monitor executing the called monitor procedure. Now, if two threads are in the monitor (i.e., they are executing two, possibly the same, monitor procedures), some private data may be modified by both threads at the same time causing race conditions to occur. Therefore, to guarantee the integrality of the private data, a monitor enforces mutual exclusion implicitly. More precisely, if a thread calls a monitor procedure, this thread will be blocked if there is another thread executing in the monitor. Those threads that were not granted the entering permission will be queued to a monitor entry queue outside of the monitor. When the monitor becomes empty (i.e., no thread is executing in it), one of the threads in the entry queue will be released and granted the permission to execute the called monitor procedure. Although we say "entry queue," you should not view it literally. More precisely, when a thread must be released from the entry queue, you should not assume any policy for which thread will be released.
In summary, monitors ensure mutual exclusion automatically so that there is no more than one thread can be executing in a monitor at any time. This is a very usably and handy capability.
The concept of a monitor is very similar to an operating system. One can consider the initialization as those data that are initialized when the system is booted up, the private data and code as the internal data structures and functions of an operating system, and the monitor procedures as the system calls. User programs are, of course, threads that make service requests. Therefore, a monitor can be considered a mini-OS with limited services.