As mentioned in the previous session, there are advanced synchronization tools available in most programming languages (or in libraries that are accessible in those programming languages) for synchronizing threads. These avoid busy waiting for threads that are blocked from gaining access to a resource (a section of code can be thought of as a resource). Such a thread or process is "put to sleep" (e.g., put in a waiting queue on some event) by the OS. Then, when that event occurs, the OS can (depending on how it is designed to handle events) remove all processes from the queue awaiting the event and move them to the ready to run queue.
Example: When a semaphore is checked, this may be implemented as a call to the OS which is managing all semaphores. If the sempahore value is not greater than 0, the OS puts the thread or process that was checking the semaphore into an queue awaiting the event that the semaphore value has been incremented. Note that the value of the semaphore not being positive means that some other threads or processes are accessing the shared resource at that moment, and that no more threads or processes are allowed to do so. Eventually, one of those threads or processes will relinquish control of the shared resource by signalling on the semaphore. Again, this will be a call to the OS which will increment the value of the semaphore, check to see whether any processes are blocked on that semaphore, and, if so, place them into the ready to run queue. One of them will then be the first to check the semaphore again, find it nonzero, and be able to gain access to the shared resource.
The sempahore, as a synchronization construct for managing concurrent threads and processes, represented a significant contribution, attributed to Edsgar W. Dijkstra.