Use case for Lock.tryLock()
One straight-forward use case is a thread processing a batch of elements, occasionally trying to commit the elements that have been processed. If acquiring the lock fails, the elements will be committed in the next successful attempt or at the final, mandatory commit.
Another example can be found within the JRE itself, ForkJoinTask.helpExpungeStaleExceptions()
is a method for performing a task that can be done by an arbitrary thread, but only one at a time, so only the one thread successfully acquiring the lock will perform it, all others will return, as the unavailability of the lock implies that there is already a thread performing the task.
It is possible to implement a similar feature before Java 5, if you separate the intrinsic locking feature, which doesn’t support being optional, from the locking logic, that can be represented as an ordinary object state. This answer provides an example.
My question is, does this use case not existed before Java 5 or folks used to implement it via some other techniques?
The Lock
interface was added in Java 5, is that what you mean? Not sure what was there before.
I am not able to comprehend the need to execute perform alternative actions based on lock availability. Can somebody please explain real use cases for this?
Sure. Just wrote one of these today actually. My specific Lock
implementation is a distributed lock that is shared among a cluster of servers using the Jgroups protocol stack. The lock.tryLock(...)
method makes RPC calls to the cluster and waits for responses. It is very possible that multiple nodes maybe trying to lock and their actions might clash causing delays and certainly one lock to fail. This either could return false
or timeout in which case my code just waits and tries again. My code is literally:
if (!clusterLock.tryLock(TRY_LOCK_TIME_MILLIS, TimeUnit.MILLISECONDS)) {
logger.warn("Could not lock cluster lock {}", beanName);
return;
}
Another use case might be a situation where one part of the code holds a lock for a large amount of time and other parts of the code might not want to wait that long and instead want to get other work done.
Here's another place in my code where I'm using tryLock(...)
// need to wait for the lock but log
boolean locked = false;
for (int i = 0; i < TRY_LOCK_MAX_TIMES; i++) {
if (lock2.tryLock(100, TimeUnit.MILLISECONDS)) {
logger.debug("Lock worked");
locked = true;
break;
} else {
logger.debug("Lock didn't work");
}
}
The reason for writing code like that example is if you have a thread that is doing more than one job.
Imagine you put it in a loop:
while (true) {
if (taskA_needsAttention() && taskA_lock.tryLock()) {
try {
...do some work on task A...
} finally {
taskA_lock.unlock();
}
} else if (taskB_needsAttention() && taskB_lock.tryLock()) {
try {
...do some work on task B...
} finally {
taskB_lock.unlock();
}
} else ...
}
Personally, I would prefer not to write code like that. I would prefer to have different threads responsible for task A and task B or better still, to use objects submitted to a thread pool.