CodeGym /Java Blog /Multithreading in Java /Difference Between a Mutex, a Monitor, and a Semaphore
Author
Oleksandr Miadelets
Head of Developers Team at CodeGym

Difference Between a Mutex, a Monitor, and a Semaphore

Published in the Multithreading in Java group
Hi! When you studied multithreading on CodeGym, you frequently encountered the concepts of "mutex" and "monitor". Without peeking, can you say how they differ? :) If yes, well done! If not (this is most common), that's no surprise. "Mutex" and "monitor" are actually related concepts. Additionally, when you read lessons and watch videos about multithreading on other websites, you'll come across another similar concept: "semaphore". It also has a very similar function to monitors and mutexes. That's why we're going to investigate these three terms. We'll look at a few examples and come to a definitive understanding of how these concepts differ from one another :)

Mutex

A mutex (or lock) is a special mechanism for synchronizing threads. One is "attached" to every object in Java — you already know that :) It doesn't matter if you use standard classes or create your own classes, e.g. Cat and Dog: all objects of all classes have a mutex. The term "mutex" comes from "MUTual EXclusion", which perfectly describes its purpose. As we said in one of our previous lessons, a mutex makes it possible to ensure that only one thread at a time has access to the object. A popular real-life example of a mutex involves toilets. When a person enters a toilet partition, he locks the door from the inside. The toilet is like an object that can be accessed by multiple threads. The lock on the partition door is like a mutex, and the line of people outside represents threads. The lock on the door is the toilet's mutex: it ensures that only one person can get inside. What's the difference between a mutex, a monitor, and a semaphore? - 2In other words, only one thread at a time can work with shared resources. Attempts by other threads (people) to gain access to occupied resources will fail. A mutex has several important features. First, only two states are possible: "unlocked" and "locked". This helps us understand how it works: you can draw parallels with Boolean variables (true/false) or binary numbers (0/1). Second, the state cannot be controlled directly. Java has no mechanism that would let you explicitly take an object, get its mutex, and assign the desired status. In other words, you can't do something like:

Object myObject = new Object();
Mutex mutex = myObject.getMutex();
mutex.free();
This means that you can't release an object's mutex. Only the Java machine has direct access to it. Programmers work with mutexes through the tools of the language.

Monitor

A monitor is an additional "superstructure" over a mutex. In fact, a monitor is a chunk of code that is "invisible" to the programmer. When we spoke about mutexes earlier, we gave a simple example:

public class Main {

   private Object obj = new Object();

   public void doSomething() {

       // ...some logic, available for all threads

       synchronized (obj) {

           // Logic available to just one thread at a time
       }
   }
}
In the code block marked with the synchronized keyword, the mutex of our obj object is acquired. Great, we can acquire the lock, but how exactly is the "protection" provided? When we see the word synchronized, what prevents the other threads from entering the block? The protection comes from a monitor! The compiler converts the synchronized keyword into several special pieces of code. Once again, let's return to our example with the doSomething() method. We'll add to it:

public class Main {

   private Object obj = new Object();

   public void doSomething() {

       // ...some logic, available for all threads

       // Logic available to just one thread at a time
       synchronized (obj) {

           /* Do important work that requires that the object
           be accessed by only one thread */
           obj.someImportantMethod();
       }
   }
}
Here is what happens "under the hood" after the compiler converts this code:

public class Main {

   private Object obj = new Object();

   public void doSomething() throws InterruptedException {

       // ...some logic, available for all threads

       // Logic available to just one thread at a time:
     
       /* as long as the object's mutex is busy,
       all the other threads (except the one that acquired it) are put to sleep */
       while (obj.getMutex().isBusy()) {
           Thread.sleep(1);
       }

       // Mark the object's mutex as busy
       obj.getMutex().isBusy() = true;

       /* Do important work that requires that the object
       be accessed by only one thread */
       obj.someImportantMethod();

       // Free the object's mutex
       obj.getMutex().isBusy() = false;
   }
}
Of course, this isn't a real example. Here, we used Java-like code to depict what happens inside the Java machine. That said, this pseudo-code gives an excellent understanding of what actually happens with the object and threads inside the synchronized block and how the compiler converts this keyword into several statements that are "invisible" to the programmer. Basically, Java uses the synchronized keyword to represent a monitor. All the code that appears instead of the synchronized keyword in the last example is the monitor.

Semaphore

Another word that you'll encounter in your personal study of multithreading is "semaphore". Let's figure out what this is and how it differs from a monitor and a mutex. A semaphore is a tool for synchronizing access to some resource. Its distinctive feature is that it uses a counter to create the synchronization mechanism. The counter tells us how many threads can simultaneously access the shared resource. What's the difference between a mutex, a monitor, and a semaphore? - 3Semaphores in Java are represented by the Semaphore class. When creating semaphore objects, we can use the following constructors:

Semaphore(int permits)
Semaphore(int permits, boolean fair)
We pass the following to the constructor:
    int permits — the initial and maximum value of the counter. In other words, this parameter determines how many threads can simultaneously access the shared resource;
  • boolean fair — establishes the order in which threads will gain access. If fair is true, then access is granted to waiting threads in the order in which they requested it. If it is false, then the order is determined by the thread scheduler.
A classic example of semaphore use is the dining philosopher problem. What's the difference between a mutex, a monitor, and a semaphore? - 4To facilitate understanding, we'll simplify it a little bit. Imagine that we have 5 philosophers who need to eat lunch. Additionally, we have one table that can simultaneously accommodate no more than two people. Our task is to feed all the philosophers. None of them should go hungry, and none of them should "block" each other when trying to sit down at the table (we must avoid deadlock). Here's what our philosopher class will look like:

class Philosopher extends Thread {

   private Semaphore sem;

   // Did the philosopher eat?
   private boolean full = false;

   private String name;

   Philosopher(Semaphore sem, String name) {
       this.sem=sem;
       this.name=name;
   }

   public void run()
   {
       try
       {
           // If the philosopher has not eaten
           if (!full) {
               // Ask the semaphore for permission to run
               sem.acquire();
               System.out.println(name + " takes a seat at the table");

               // The philosopher eats
               sleep(300);
               full = true;

               System.out.println(name + " has eaten! He leaves the table");
               sem.release();

               // The philosopher leaves, making room for others
               sleep(300);
           }
       }
       catch(InterruptedException e) {
           System.out.println("Something went wrong!");
       }
   }
}
And here's the code to run our program:

public class Main {

   public static void main(String[] args) {

       Semaphore sem = new Semaphore(2);
       new Philosopher(sem, "Socrates").start();
       new Philosopher(sem,"Plato").start();
       new Philosopher(sem,"Aristotle").start();
       new Philosopher(sem, "Thales").start();
       new Philosopher(sem, "Pythagoras").start();
   }
}
We created a semaphore whose counter is set to 2 to satisfy the condition: only two philosophers can eat at the same time. That is, only two threads can run at the same time, because our Philosopher class inherits Thread! The acquire() and release() methods of the Semaphore class control its access counter. The acquire() method asks the semaphore for access to the resource. If the counter is >0, then access is granted and the counter is reduced by 1. The release() method "releases" the previously granted access, returning it to the counter (increases the semaphore's access counter by 1). What do we get when we run the program? Is the problem solved? Will our philosophers not fight while they wait for their turn? :) Here's is the console output we got:

Socrates takes a seat at the table 
Plato takes a seat at the table 
Socrates has eaten! He leaves the table 
Plato has eaten! He leaves the table 
Aristotle takes a seat at the table 
Pythagoras takes a seat at the table 
Aristotle has eaten! He leaves the table 
Pythagoras has eaten! He leaves the table 
Thales takes a seat at the table 
Thales has eaten! He leaves the table 
We did it! And though Thales had to dine alone, I don't think we've offended him :) You may have noticed some similarities between a mutex and a semaphore. Indeed, they have the same mission: to synchronize access to some resource. What's the difference between a mutex, a monitor, and a semaphore? - 5The only difference is that an object's mutex can be acquired by only one thread at a time, while in the case of a semaphore, which uses a thread counter, several threads can access the resource simultaneously. This isn't just a coincidence :) A mutex is actually a semaphore with a count of 1. In other words, it's a semaphore that can accommodate a single thread. It's also known as a "binary semaphore" because its counter can have only 2 values — 1 ("unlocked") and 0 ("locked"). That's it! As you can see, it's not so confusing after all :) Now, if you want to study multithreading in more detail on the Internet, it will be a little easier for you to navigate these concepts. See you in the next lessons!
Comments (9)
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION
manny9876 Level 34, Israel
23 October 2023
Perfectly explained! I wish all of codegym was written this clear! Here is a summary by Chatgpt: Mutex: A mutex is like a "key" that allows only one thread to access a shared resource at a time. Monitor: A monitor is a "guard" that ensures only one thread can access a resource, and it often surrounds synchronized blocks or methods to maintain exclusive access. Semaphore: A semaphore is like a "traffic controller" that can allow a specified number of threads to access a resource simultaneously, unlike a mutex which only permits one. Difference: Mutex allows exclusive access to one thread, while a semaphore can permit multiple threads as long as they don't exceed a set limit. A monitor is more of a general concept for ensuring synchronized access, while mutex and semaphore are specific synchronization constructs.
Tomek Level 20, Poland Expert
18 August 2022
Perfect lesson, clearly explained!
Shilpa nori Level 34, Rochester, United States
25 February 2021
Great lesson!
Lawson Level 29, Lagos, Nigeria
10 September 2020
Decide to try this code and for some reason, it doesnt work.. any help public class Philosopher extends Thread { private Semaphore sem; // Did the philosopher eat? private boolean full = false; private String name; public Philosopher(Semaphore sem, String name) { this.sem=sem; this.name=name; } public void run() { try{ if(!full){ sem.acquire(); System.out.println(name +" wants to start eating"); sleep(300); full = true; System.out.println(name +" has eaten!He leaves the table"); sem.release(); sleep(300); } }catch (Exception e){ System.out.println("Something went wrong"); } } } public class Program{ public static void main(String[] args){ Semaphore sem = new Semaphore(2); new Philosopher(sem,"Aristotle").start(); new Philosopher(sem,"Socrates").start(); new Philosopher(sem,"Plato").start(); new Philosopher(sem, "Thales").start(); new Philosopher(sem, "Pythagoras").start(); } }
MaGaby2280 Level 41, Guatemala City, Guatemala
10 June 2020
Now this was a great lesson, clearly explaned all the concepts!!!
Chris Hilborne Level 26, Sanlucar de Barrameda, Spain
7 June 2020
Wait - so does the Philosopher run() method just wait at line 22 "sem.aquire();" until is is given access?
WIDMO Level 25, Gdańsk, Poland Expert
3 June 2020
There should be more lessons like this one. The "Semaphore" part was masterpiece.
Niklas Braun Level 33, Freiburg, Germany
30 May 2020
great lesson!