In Java, when we have to lock a resource, synchronize keyword is used. In recent versions, we can use ReentrantLocks. However with synchronize of ReentrantLocks approach, it's a binary approach. We either get the lock to resource we do not get it. What to do when we have more than one resources and want to allocate them till they exhaust. For example, let's say there are three ATM machines and we want to server the queue of users wanting to withdraw money. Locking a ATM machine will only allow one user to access it in the synchronize way. The other way is to define three ATM machines and let each be handled separately.
To deal with such situations elegantly, Java has the notion of semaphore. Semaphore is a basic construct of concurrent programming and Java also supports it. It's a better way to handle shared resources. Let's see the ATM example with semaphore.
Let's define an ATMCounters class which takes the number of ATM as arguments. Note the usage of Semaphore.
public class ATMCounters {
//Semaphore is the lock
private Semaphore semapahore;
public ATMCounters(int numberOfCounters){
//Number of resources available is passed as argument
semapahore = new Semaphore(numberOfCounters);
}
public void withdrawMoney(ATMUser atmUser){
try{
//Acquire the thread. It will acquire only if
//resources are available
semapahore.acquire();
System.out.println("User: " + atmUser.getUserNumber() + " withdrawing money");
//Put a random sleep
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
System.out.println
("User: " + atmUser.getUserNumber() + " money withdrawn");
System.out.println
("Sempahore resource count :" + semapahore.availablePermits());
//Make sure to release the semaphore
semapahore.release();
}
}
}
Now let's define the ATMUser class. It represents the user who is going to withdraw money. ATMUser class implements runnable so can be run as a thread. In the thread, the object withdraws the money from ATMCounters.
public class ATMUser implements Runnable {
private int userNumber;
private ATMCounters atmCounters;
public ATMUser(int userNo,ATMCounters atmCntrs) {
this.userNumber = userNo;
this.atmCounters = atmCntrs;
}
@Override
public void run() {
atmCounters.withdrawMoney(this);
}
public int getUserNumber() {
return userNumber;
}
}
And the main method
public class SemaphoreMain {
public static void main(String[] args) {
//Let's have three ATM counters
ATMCounters atmCounters = new ATMCounters(3);
//Create 10 users and ask them to withdraw money
for(int i =0 ; i < 10; i++){
Thread thread = new Thread(new ATMUser(i,atmCounters));
thread.start();
}
}
}
When the program is run the output is as follows. Note that at no moment more than three user are withdrawing money.
User: 0 withdrawing money
User: 5 withdrawing money
User: 1 withdrawing money
User: 0 money withdrawn
Sempahore resource count :0
User: 5 money withdrawn
Sempahore resource count :1
User: 4 withdrawing money
User: 9 withdrawing money
User: 1 money withdrawn
Sempahore resource count :0
User: 2 withdrawing money
User: 9 money withdrawn
Sempahore resource count :0
User: 7 withdrawing money
User: 4 money withdrawn
Sempahore resource count :0
User: 8 withdrawing money
User: 2 money withdrawn
Sempahore resource count :0
User: 3 withdrawing money
User: 7 money withdrawn
Sempahore resource count :0
User: 6 withdrawing money
User: 8 money withdrawn
Sempahore resource count :0
User: 3 money withdrawn
Sempahore resource count :1
User: 6 money withdrawn
Sempahore resource count :2
No comments:
Post a Comment