Main Components of the Concurrency Utilities - 1 - Semaphore
Start course

In this course, we will learn the concepts of Java EE 7 with a focus on Concurrency Utilities with Threads, Semaphore, Phaser and other methods, and Transactions.

Learning Objectives

  • Concurrency Utilities 

Intended Audience

  • Anyone looking to get Oracle Java Certification
  • Those who want to improve Java 7 EE knowledge
  • Java developers


  • Have at least 2 years of Java development experience 

Hello there. In this lesson, we will talk about concurrency in Java, so let's start. From the beginning, Java supported concurrency in the form of low level threads management, locks, synchronization and APIs for concurrency. We cover them in the preceding chapter in our discussion of the threads class, Runnable Interface and Synchronized Keyword. Since Java 5.0, Java also supports high-level concurrency APIs in its java.util.concurrent package. In this lesson, we'll focus on these APIs for concurrent programming. These high level APIs exploit today's multi-core hardware in which a single processor has multiple cores. These APIs are also useful for exploiting concurrency in machines that support multiple processors. Most of the Java concurrency utilities are provided in the java.util.concurrent package. Classes to efficiently update shared variables without using locks are provided in the java.util.concurrent.atomic sub-package.

The lock interface and the classes deriving from it are provided in the java.util.concurrent.locks sub-package. There are many cases in the java.util.concurrent package that provide high level APIs for concurrent programming. In this section, we will mainly discuss synchronizer classes provided in this package. Following that, we will briefly cover the important concurrent collection classes provided in the java.util.concurrent package. You already understand the low level concurrency constructs such as the use of the synchronized keyword, runnable interface and thread class for creating threads from the preceding chapter. In the case of a shared resource that needs to be accessed by multiple threads, access and modifications to the shared resource need to be protected. When you use the synchronized keyword, you employ mutexes to synchronize between threads for safe shared access.

Threads also often needed to coordinate their executions to complete a bigger higher level task. The wait notified pattern discussed in the last chapter is one way to coordinate the execution of multiple threads. Using APIs for acquiring and releasing locks using mutexes or invoking the wait notify methods on locks are low-level tasks. It's possible to build higher level abstractions for thread synchronization. These high-level abstractions for synchronizing activities of two or more threads are known as synchronizers. Synchronizers internally make use of the existing low-level APIs for thread coordination. The synchronizer is provided in the java.util.concurrent library and their uses are listed here. Semaphore controls access to one or more shared resources. Phaser is used to support a synchronization barrier. CountDownLatch allows for threads to wait for a countdown to complete. CyclicBarrier enables threads to wait at a predefined execution point. Now we'll discuss each of these synchronizers in turn with the help of examples. Semaphore. A semaphore controls access to shared resources.

A semaphore maintains a counter to specify the number of resources that the semaphore controls. Access to the resource is allowed if the counter is greater than zero while a zero value of the counter indicates that no resources available at the moment, so the access is denied. The methods acquire and release are for acquiring and releasing resources from a semaphore. If a thread calls acquire and the counter is zero, the thread waits until the counter is non-zero and then gets the resource for use. Once the thread is done using the resource, it calls release to increment the resource availability counter. Note if the number of resources is one, then at any given time, only one thread can access the resource. In this case, using the semaphore is similar to using a lock. Semaphore. Constructor to create semaphore objects with a given number of permits. If the permits' value is negative, the given number of release calls must happen before acquire calls can succeed. Semaphore. Same as the previous constructor, but this extra fair option indicates that permits should be allotted on a first come first served basis. Void acquire.

Acquires a permit if available, otherwise, it blocks until a permit becomes available. Can throw an interrupted exception if some other thread interrupts it while waiting to acquire a permit. The overloaded version takes a number of permits as an argument. Void acquireUninterruptibly. Same as the acquire method, but this thread cannot be interrupted while waiting to acquire a permit. Boolean tryAcquire. Acquires a permit from the semaphore if available at the time of the call and returns true. If unavailable, it returns false immediately. The overloaded tryAcquire method additionally takes a timeout argument. The thread blocks to acquire a permit from the semaphore until a given timeout period. Void release. Releases a permit from the semaphore. The overloaded version specifies the number of permits to release. Let's move on to the Eclipse. We'll create a Java class in this project. And lastly, I will check the box public static void main to let Eclipse create the main method. Let's assume there are two ATM machines available in an ATM machine room.

Therefore, only two people are allowed at a time in the room. There are five people waiting outside to use the ATM machines. The situation can be simulated by the code in here, in which each ATM machine is treated as a resource controlled by semaphore. This class simulates a situation where an ATM room has only two ATM machines and five people are waiting to access the machine. Since only one person can access an ATM machine at a given time, others wait for their turn. Assume that only two ATM machines are available in the ATM room. List of people waiting to access the machine. Each person is an independent thread, but their access to the common resource two ATM machines in the ATM machine room in this case needs to be synchronized. The output is like this. Now, let's analyze how this program works. People waiting to access an ATM machine are simulated by creating a person class that extends thread.

The run method in the thread class acquires a semaphore, simulates withdrawing money from the ATM machine, and releases the semaphore. The main method simulates an ATM room with two ATM machines by creating a semaphore object with two permits. People waiting in the queue to access the ATM machine are implemented by just adding them to the semaphore object. As you can see from the program output, the semaphore allows only two threads at a time, and the other threads keep waiting. When a thread releases the semaphore, another thread acquires it. So, that's it. Hope to see you in our next lesson. Have a nice day.


About the Author
Learning Paths

OAK Academy is made up of tech experts who have been in the sector for years and years and are deeply rooted in the tech world. They specialize in critical areas like cybersecurity, coding, IT, game development, app monetization, and mobile development.

Covered Topics