Java locks often supersede synchronized blocks and methods by allowing to back out from the synchronization attempt. This post shows how to use the feature, and demonstrates some other basic concepts, like the lock fairness and reentrant locks, which should help you to get started with the lock-based synchronization.
The reentrant lock
ReentrantLock is the most often used implementation of the
Lock interface. Reentrant means that the
same thread can acquire a lock multiple times, which could be extremely handy when you need to chain synchronized method
calls. The only caveat here is that the lock must be released the same number of times it was requested by a thread:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
As you can see, the
ReentrantLock implementation allows us to call the
lock.lock() two times from the one thread,
and execute the bar from the locked block of the foo method.
The second concept, we will have a look at, is the lock fairness. Lock fairness is quite easy to grasp, but when used incorrectly can lead to confusing, blocking-alike issues.
The expected behaviour for threads is to acquire a lock in the same order they ask for it. But, in case of the unfair lock this order is not guaranteed, and a thread can get a lock before the other thread that asked for the lock first. As you have already guessed, there is a risk that one of the threads will never acquire the lock.
So, usually the rule of thumb is to set the ordering policy to fair when instantiating a lock object:
The last concept and one of the main benefits of the Java locks is the try lock mechanism. The try lock method
allows to back out of an attempt to acquire a lock if the lock is taken by another thread. Also, the method accepts
time parameter, which can be especially useful when you need to limit the time a thread waits for a lock in
order to speed up the application or to avoid deadlocks. For instance:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
1 2 3
Results in the following output:
1 2 3 4 5
Here, as the lock is not available and is already taken by the first thread, the
tryLock method called from the
second thread backs out to the else block after one second of waiting.
Thread synchronization techniques are currently in a gradual decline, replaced by implementations of non-blocking asynchronous concepts, such as Actors or Agents. However, if you have a Java application that heavily relies on the synchronization, you can still get a sufficient flexibility with Java locks.