Message ordering with Java Semaphores
Semaphores are useful to limit the access of concurrent threads to a resource.
This can be: A class instance, a variable or a method.
You can use it to assert that in critical parts of your code no parallel requests are happening.
Example use-case: Ordered messages
I used Semaphores in the financial world to assure, that time-sensitive messages had been send to an external resource in a specific order.
Every message had an order number which was counted up on every send.
In this specific case it was important to:
- Assure that the order of the calculated data is correct.
- Assure that no order number was sent twice.
- Many messages could be send at the same time without getting a destroyed order
The following method shows an example how it could have been done:
private int orderNumber = 0;
public void send(Message message) {
Integer current = ++orderNumber;
}
In this case the order number is a class field, which just gets count up on every send method call and stored in a temp variable.
But when you get asked how you assure the requirements from above, you can’t answer the questions!
That’s where we can use Semaphores.
Java Semaphores
Semaphores get initialized with a new Semaphore(...) call. For example:
Semaphore semaphore = new Semaphore(1);
Everytime a Thread tries to acquire a resource, it must ask for a permit. The number 1 tells the Semaphore, that only one permit is free - so that only one thread can access a resource at the same time. This is also called a binary semaphore.
To acquire a permit the thread must call the acquire() method of the semaphore. If it is not available, the thread will block until it will be.
As soon as the Thread gets the resource, it will proceeds it work and must release the resource via a release() call.
There are also some concepts like tryAcquire(), where a Thread tries to acquire a permit and otherwise just stops. And a concept of fairness (which is not part of this article).
Example with Semaphores
So how to solve the problem from above in a good manner with Semaphores?
First we will implement the binary semaphore and the order number as class fields:
Semaphore semaphore = new Semaphore(1);
private int orderNumber = 0;
In the next step we should implement a method which retrieves the current count.
Check the code with comments as an example implementation:
private Integer currentOrderUp() {
try {
semaphore.acquire(); // acquire permission. If it is not free: wait
return ++orderNumber; // count up order number and return
} catch (InterruptedException e) {
System.err.println("...");
} finally {
semaphore.release(); // in any cases: release it
}
return null; // if something failed: return null
}
You can see that the count up happens only once at the same time, even if concurrent threads try to access the same variable.
We do it by acquiring the semaphore and then counting up.
As soon as this is done, we release it. And even more: We release it also when there is an Exception. This is really important as otherwise our application would block.
If something fails, we will return per contract null which can be used as a fallback or retry indicator.
The send method would look like the following:
public void send(Message message) {
Integer current = currentOrderUp();
if (current == null) {
// something went wrong; retry
}
// Do other - maybe also longer running jobs here.
}
So when concurrent access happens on the send method, we assure now, that the critical part - the count up of the order number - is executed by just one Thread.