【问题标题】:Use case for Lock.tryLock()Lock.tryLock() 的用例
【发布时间】:2017-06-06 21:13:05
【问题描述】:

在Lockinterface的API docs方法tryLock()处,粘贴此代码示例,

这种方法的典型用法是:

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

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

我无法理解根据锁定可用性执行执行替代操作的必要性。

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

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

【问题讨论】:

  • 对于否决投票者和投票关闭它的人,如果您感觉如此,请恢复您的操作,因为它已经超过两天并且您没有足够的接受者。此处发布的两个答案提供了非常有用的见解,我希望我的问题能引起注意,但我想否定投票是一个障碍,因为大多数人不喜欢回答被否决的问题。如果您不这么认为,请不要介意。

标签: java multithreading concurrency locking


【解决方案1】:

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

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


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

【讨论】:

  • 你能提供直截了当的例子吗?描述对我来说并不明显
  • 我不是 ForkJoinPool 内部的专家,因此很难理解为什么在这种情况下使用 tryLock
  • 好吧,正如我已经说过的,expungeStaleExceptions() 是“一种用于执行可由任意线程完成的任务的方法,但一次只能完成一个”。没有必要了解更多细节。我不明白,如果你不明白,我怎么能帮助你。任何现实生活中的例子都太复杂了,我可以发布的任何例子都太做作了。
  • 此描述尊重通常的相互排斥,如同步
  • 是的,这是互斥的,就像synchronized。不同之处在于synchronized,所有线程都将被阻塞,直到执行工作的线程完成,而tryLock,当其他人已经在做工作时,所有其他线程立即返回给调用者。
【解决方案2】:

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

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");
    }
}

【讨论】:

  • 嗨,格雷!你能分享一些关于你的“在服务器集群之间共享”锁的信息吗?我很想知道如何建造这样的东西!真的!谢谢!
  • 它基于 Jgroups @LppEdd。它们的基本代码中有许多集群操作,例如分布式锁。
【解决方案3】:

编写类似该示例的代码的原因是,如果您有一个线程在做不止一项工作。

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

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

【讨论】:

  • 谢谢,有道理!!
【解决方案4】:

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

【讨论】:

    猜你喜欢
    • 2017-12-23
    • 2020-02-16
    • 2017-08-05
    • 1970-01-01
    • 2014-07-21
    • 1970-01-01
    • 2013-04-06
    • 1970-01-01
    • 2022-11-09
    相关资源
    最近更新 更多