This example is another one built over counting semaphores. However, its purpose is to illustrate how individual mutexes and condition variables can be used rather than to illustrate the semaphores themselves. "Mutex" and "CondVar" build mutexes and condition variables using the "CountingSemaphore" class, following the design presented in the lecture notes. For example, a "Mutex" is implemented using a single counting semaphore which is initialized to the value 1 and is locked by performing a "P" operation on the semaphore and unlocked by performing a "V" operation. The "Buffer" interface defines a simple single-cell shared buffer holding integer values. "BufferBasic" implements the buffer using one mutex and one condition variable. The implementation therefore follows the usual Java one very closely, except that the mutex and condition variable are accessed explicitly through fields, rather than using the alternative built-in concurrency control primitives associated with Java objects. "BufferMultipleCondvars" shows an alternative implementation in which two condition variables are used. One of these is used to represent the 'buffer full' condition and the other is used to represent the 'buffer empty' condition. Although this makes the buffer's implementation more complicated, it means that notifications can be directed more precisely. For example, when a "put" operation has succeeded, only the threads blocked on the 'buffer empty' condition can be notified. This technique can be useful in more complex data structures, or when there are a lot of threads, because it reduces the number of threads which are needlessly woken. "BufferExample1" runs the example using the basic buffer implementation. "BufferExample2" runs the example using the multiple-condvars implementation.