how do other thread(s)get notified,
Virtually all practical JVMs let the operating system do the real work. workstations, servers, and mobile devices all run preemptive, multi-tasking operating systems these days, and it's the operating system that provides the primitive functions upon which threads, mutexes, semaphores, etc. are all built.
The most important thing that the OS can do that programs can't do for themselves is called a context switch. That's where the OS suspends one thread, saves its state in a special "context record," and then restores the context of some other thread, allowing the second thread to start using the same CPU that the first thread previously was using.
Whenever a thread waits to lock a mutex, waits for I/O, for some_object.notify()
, or for pretty much anything else, the OS "switches out" its context, places it in a queue of things that are awaiting that event, and we say that the thread is blocked on the event. When the event happens (e.g., when somebody releases a mutex) the OS moves the thread's context into a queue of threads that are RUNNABLE, and eventually, when a CPU becomes available, it "switches in" the context, and the thread gets to run again.
do they implement semaphores under the hood?
Semaphore
is a very old idea: It originally was meant to be a low-level primitive operation--one that could be implemented in an arcane way, without any hardware support--upon which other things like mutual exclusion, queues, barriers, etc. could be built.
These days, mutexes are the lowest level--implemented using special hardware instructions--and a semaphore is a higher level object that is built on top of a mutex. One of the main reasons we still have Semaphore
is just that so much of the existing literature talks about semaphores, and so much existing code still uses semaphores.