Lock.tryLock()的用例

时间:2017-01-22 06:40:53

标签: java multithreading concurrency locking

方法Lock的{​​{1}}接口的API docs,此代码示例已粘贴,

  

此方法的典型用法习惯是:

tryLock()

我的问题是,这个用例在Java 5之前是不存在的还是人们通过其他技术实现它?

我无法理解是否需要根据锁定可用性执行执行替代操作

有人可以为此解释实际用例吗?

我确信这种技术不能直接替代 Lock lock = ...; if (lock.tryLock()) { try { // manipulate protected state } finally { lock.unlock(); } } else { // perform alternative actions } 来编写无死锁代码。

4 个答案:

答案 0 :(得分:3)

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

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

如果将内部锁定功能(不支持可选)与锁定逻辑分开(可以表示为普通对象状态),则可以在Java 5之前实现类似功能。 This answer提供了一个示例。

答案 1 :(得分:2)

编写类似代码的代码的原因是,如果您有一个执行多个作业的线程。

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

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,或者更好的是,使用提交到线程池的对象

答案 2 :(得分:2)

  

我的问题是,这个用例在Java 5之前是不存在的还是人们通过其他技术实现它?

在Java 5中添加了Lock接口,这是什么意思?不知道之前有什么。

  

我无法理解是否需要根据锁定可用性执行替代操作。有人可以为此解释实际用例吗?

不确定。实际上今天就写了其中一个。我的特定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");
    }
}

答案 3 :(得分:0)

用例 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