Use case for Lock.tryLock()



在方法tryLock()Lock接口的API文档中,粘贴此代码示例,

此方法的典型用法是:

Lock lock = ...;
if (lock.tryLock()) {
try {
// manipulate protected state
} finally {
lock.unlock();
}
} else {
// perform alternative actions
}

我的问题是,在Java5之前,这个用例是否不存在,或者人们过去通过一些其他技术来实现它?

我无法理解是否需要执行基于锁可用性执行替代操作

有人能解释一下这个的真实用例吗?

我确信这种技术并不能直接取代synchronized来编写无死锁代码。

一个直接的用例是线程处理一批元素,偶尔尝试提交已处理的元素。如果获取锁失败,则元素将在下一次成功尝试或最后的强制提交时提交。

另一个例子可以在JRE本身中找到,ForkJoinTask.helpExpungeStaleExceptions()是一种执行任务的方法,该任务可以由任意线程完成,但一次只能由一个线程完成,因此只有成功获取锁的一个线程才会执行,所有其他线程都会返回,因为锁的不可用意味着已经有线程在执行该任务。


如果将不支持可选的内部锁定功能与可以表示为普通对象状态的锁定逻辑分离,那么在Java 5之前就可以实现类似的功能。这个答案提供了一个例子。

我的问题是,在Java5之前,这个用例是否不存在,或者人们过去通过一些其他技术来实现它?

Lock接口是在Java 5中添加的,这就是你的意思吗?不确定之前有什么。

我无法理解是否需要执行基于锁可用性的替代操作。有人能解释一下这个的真实用例吗?

当然。事实上,今天刚刚写了一篇。我的特定Lock实现是一个分布式锁,它在使用Jgroups协议栈的服务器集群之间共享。lock.tryLock(...)方法对集群进行RPC调用并等待响应。多个节点可能试图锁定,而它们的操作可能会发生冲突,从而导致延迟,当然还有一个锁定失败。这可能会返回false或超时,在这种情况下,我的代码只是等待并重试。我的代码是:

if (!clusterLock.tryLock(TRY_LOCK_TIME_MILLIS, TimeUnit.MILLISECONDS)) {
logger.warn("Could not lock cluster lock {}", beanName);
return;
}

另一个用例可能是这样的情况:代码的一部分持有锁很长时间,而代码的其他部分可能不想等待那么长时间,而是想完成其他工作。

这是我代码中使用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");
}
}

编写类似该示例的代码的原因是,如果您有一个线程正在执行多个任务。

想象一下,你把它放在一个循环中:

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 ...
}

就我个人而言,我宁愿不写这样的代码。我更喜欢让不同的线程负责任务A和任务B,或者更好的是,使用提交到线程池的对象。

用例1

一个用例是完全避免运行线程。就像下面的例子一样,例如一个非常严格的互联网热点,你一次只能访问一个网页,其他请求都会被取消。

对于synchronized,您不能取消它,因为它会等待,直到它可以获得锁。因此,tryLock只是给了您取消某些内容或运行其他行为的灵活性。

package Concurrency;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LimitedHotspotDemo
{
private static Lock webAccessLock = new ReentrantLock();
private static class AccessUrl implements Runnable
{
private String url;
public AccessUrl(String url)
{
this.url = url;
}
@Override
public void run()
{
if(webAccessLock.tryLock()) {
System.out.println("Begin request for url " + url);
try {
Thread.sleep(1500);
System.out.println("Request completed for " + url);
} catch (InterruptedException e) {
webAccessLock.unlock();
return;
} finally {
webAccessLock.unlock();
}
} else {
System.out.println("Cancelled request " + url + "; already one request running");
}
}
}
public static void main(String[] args)
{
for(String url : Arrays.asList(
"https://www.google.com/",
"https://www.microsoft.com/",
"https://www.apple.com/"
)) {
new Thread(new AccessUrl(url)).start();
}
}
}

输出:

Begin request for url https://www.microsoft.com/
Cancelled request https://www.google.com/; already one request running
Cancelled request https://www.apple.com/; already one request running
Request completed for https://www.microsoft.com/

用例2

另一个用例是光传感器,当房间里有动静时,它会保持灯亮(带有Sensor线程)。当房间里几秒钟没有更多的活动时,还有另一个线程(TurnOffLights)正在运行以关闭灯。

TurnOffLights线程使用tryLock来获得锁。如果无法锁定,则进程将延迟500ms。最后一个Sensor线程正在锁定5秒,之后TurnOffLights线程可以获得锁定并关灯。

因此,在这种情况下,TurnOffLights线程仅被允许在5秒钟内没有更多信号到Sensor时关灯。TurnOffLights线程正在使用tryLock来获得锁。

package Concurrency;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LightSensorDemo
{
private static volatile Lock lock = new ReentrantLock();
private static volatile Thread lastSignal = null;
private static Sensor sensor = new Sensor();
private static class Sensor implements Runnable
{
private static Boolean preparing = false;
public static Boolean isPreparing()
{
return preparing;
}
@Override
public void run()
{
System.out.println("Signal send " + Thread.currentThread().getName());
try {
invalidatePreviousSignalsAndSetUpCurrent();
Thread.sleep(5 * 1000);
} catch (InterruptedException e) {
//System.out.println("Signal interrupted " + Thread.currentThread().getName());
return;
} finally {
lock.unlock();
}
}
private static synchronized void invalidatePreviousSignalsAndSetUpCurrent() throws InterruptedException
{
preparing = true;
if(lastSignal != null) {
lastSignal.interrupt();
}
lastSignal = Thread.currentThread();
lock.lockInterruptibly();
preparing = false;
}
}
private static class TurnOffLights implements Runnable
{
@Override
public void run()
{
while(true) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
System.out.println("Interrupted" + this.getClass().getName());
return;
}
if (!Sensor.isPreparing()) {
if(lock.tryLock()) {
try {
System.out.println("Turn off lights");
break;
} finally {
lock.unlock();
}
}  else {
System.out.println("Cannot turn off lights yet");
}
} else {
System.out.println("Cannot turn off lights yet");
}
}
}
}
public static void main(String[] args) throws InterruptedException
{
Thread turnOffLights = new Thread(new TurnOffLights());
turnOffLights.start();
//Send 40 signals to the light sensor to keep the light on
for(int x = 0; x < 10; x++) {
new Thread(sensor).start(); //some active movements
new Thread(sensor).start(); //some active movements
new Thread(sensor).start(); //some active movements
new Thread(sensor).start(); //some active movements
Thread.sleep(250);
}
turnOffLights.join();
}
}

还要注意,我使用lock.lockInterruptibly();来中断以前的信号。所以5秒倒计时总是从最后一个信号开始。

输出类似于:

...
Cannot turn off lights yet
Cannot turn off lights yet
Signal send Thread-19
Signal send Thread-20
Cannot turn off lights yet
Cannot turn off lights yet
Cannot turn off lights yet
Cannot turn off lights yet
Cannot turn off lights yet
Cannot turn off lights yet
Cannot turn off lights yet
Cannot turn off lights yet
Cannot turn off lights yet
Cannot turn off lights yet
Turn off lights
Process finished with exit code 0

最新更新