24.14. (Optional) Semaphores |
Semaphores can be used to restrict the number of threads that access a shared resource. Before accessing the resource, a thread must acquire a permit from the semaphore. After finishing with the resource, the thread must return the permit back to the semaphore, as shown in Figure 24.23.
To create a semaphore, you have to specify the number of permits with an optional fairness policy, as shown in Figure 24.24. A task acquires a permit by invoking the semaphore's acquire() method and releases the permit by invoking the semaphore's release() method. Once a permit is acquired , the total number of available permits in a semaphore is reduced by 1. Once a permit is released, the total number of available permits in a semaphore is increased by 1.
A semaphore with just one permit can be used to simulate a mutually exclusive lock. Listing 24.12 revises the Account inner class in Listing 24.10 using a semaphore to ensure that only one thread can access the deposit method at a time.
1 // An inner class for account 2 private static class Account { 3 // Create a semaphore 4 private static Semaphore semaphore = new Semaphore( 1 ); 5 private int balance = ; 6 7 public int getBalance() { 8 return balance; 9 } 10 11 public void deposit( int amount) { 12 try { 13 semaphore.acquire(); // Acquire a permit 14 int newBalance = balance + amount; 15 16 // This delay is deliberately added to magnify the 17 // data-corruption problem and make it easy to see. 18 Thread.sleep( 5 ); 19 20 balance = newBalance; 21 } 22 catch (InterruptedException ex) { 23 } 24 finally { 25 semaphore.release(); // Release a permit 26 } 27 } 28 } |
A semaphore with one permit is created in line 4. A thread first acquires a permit when executing the deposit method in line 13. After the balance is updated, the thread releases the permit in line 25. It is a good practice to always place the release() method in the finally clause to ensure that the permit is finally released even in the case of exceptions.