Below can be short definitions of these terms
- synchronized – Method can be accessed by one thread at a time
- Semaphore – Method can be accessed by n threads at a time. (n specified while initializing Semaphore)
Lets understand this with some example. Lets take example of printing. Below is a thread which takes printer & prints.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
class PrintingThread extends Thread { private Printer printer; public PrintingThread(String name, Printer printer) { this.setName(name); this.printer = printer; } @Override public void run() { printer.print(); } } |
This will be tested with simple program which creates 6 threads of this with SAME printer & starts them all.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
public class SynchronizedVsSemaphore { public static void main(String[] args) { Printer printer = new Printer(); PrintingThread printer1 = new PrintingThread("Printer 1", printer); PrintingThread printer2 = new PrintingThread("Printer 2", printer); PrintingThread printer3 = new PrintingThread("Printer 3", printer); PrintingThread printer4 = new PrintingThread("Printer 4", printer); PrintingThread printer5 = new PrintingThread("Printer 5", printer); PrintingThread printer6 = new PrintingThread("Printer 6", printer); printer1.start(); printer2.start(); printer3.start(); printer4.start(); printer5.start(); printer6.start(); } } |
Now lets design our printer …
Printer with synchronized (1 print at a time)
Lets assume that
- Our printer instance can only print one message at a time
- It has to cool down for 300 milliseconds.
So we mark printing method as synchronized so that only one thread will print at a time & others will wait.
1 2 3 4 5 6 7 8 9 10 11 12 |
class Printer { public synchronized void print() {//If lock available, grab-n-go ahead. ELSE WAIT HERE ! try { System.out.println( System.currentTimeMillis() + " | Thread " + Thread.currentThread().getName() + " printing now."); Thread.sleep(300); } catch (Exception e) { e.printStackTrace(); } } } |
Output:
Notice ~300 mills gap in each print. First is at 1543804752621 & second at 1543804752925 & so on..
1 2 3 4 5 6 |
1543804752621 | Thread Printer 1 printing now. 1543804752925 | Thread Printer 2 printing now. 1543804753233 | Thread Printer 3 printing now. 1543804753536 | Thread Printer 4 printing now. 1543804753836 | Thread Printer 6 printing now. 1543804754136 | Thread Printer 5 printing now. |
Printer with Semaphore (3 prints at a time)
Now lets assume that
- We have upgraded our printer & now it can do 3 prints at a time but not more than that.
- It still has to cool down for 300 milliseconds.
Now with synchronized we can not achieve this. We want to allow exactly 3 threads to print at the same time. Once 3 threads are in, others should keep waiting. This is where Semaphore comes to the rescue.
- While initializing Semaphore, we can mention how many locks/permits we want to allow on particular resource. In this case 3 i.e. new Semaphore(3);
- Unlike synchronized, in case of Semaphores, code needs to handle acquiring & releasing locks/permits. In case of synchronized this was taken care by JVM itself.
So our Printer looks like this now.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
class Printer { // Semaphore to allow 3 threads. private Semaphore semaphore = new Semaphore(3); public void print() { try { // Acquire one of 3 locks/permits if available. ELSE WAIT HERE ! semaphore.acquire(); System.out.println( System.currentTimeMillis() + " | Thread " + Thread.currentThread().getName() + " printing now."); Thread.sleep(300); // Release acquired lock/permit by this thread. semaphore.release(); } catch (Exception e) { e.printStackTrace(); } } } |
Output:
Notice that first 3 prints are almost similar time 1543805418613, 1543805418617, 1543805418613. Then there is ~300 mills wait & remaining are again almost same time 1543805418921.
1 2 3 4 5 6 |
1543805418613 | Thread Printer 1 printing now. 1543805418617 | Thread Printer 3 printing now. 1543805418613 | Thread Printer 2 printing now. 1543805418921 | Thread Printer 4 printing now. 1543805418921 | Thread Printer 6 printing now. 1543805418921 | Thread Printer 5 printing now. |
This means that first 3 threads were able to get into print() method at the same time & perform print activity in parallel while other 3 threads were waiting to finish first 3 threads.