是否存在超时的Java Lock实现

时间:2015-07-23 12:29:41

标签: java timeout locking java.util.concurrent rate-limiting

我有一个可以在集群环境中运行的spring应用程序。 在那种环境中,我使用Redis(和Redisson)作为分布式锁服务。

许多锁用于例如保护某些任务,这些任务一次只能运行一次,或者每隔X秒只能启动一次。

该应用程序还能够以独立模式运行(无需redis)。

但是对于这种情况,我需要一个不同的lockservice实现。 我认为这将非常简单,因为我只需要在本地创建一个具有特定超时的Lock实例(例如,对于"仅运行动作最多2分钟")。 但是当我环顾四周时,我找不到任何支持设置锁定超时的Java Lock接口的实现(以便在此之后自动解锁)。

是否有这样的事情,或者是否有一种非常简单的(就代码行而言)我自己实现这一点的方式,我只是错过了?

锁定impl应如何表现:

  • 其他线程在活动时(与任何其他锁定一样)无法锁定
  • 理想情况下,拥有线程应该能够再次调用lock(long timoutMs)来扩展锁定(将超时设置为给定时间)

编辑: 似乎一个具体的例子可以帮助理解我在寻找什么:

  • 想象一下,服务器有一个HTTP动作" doExpesiveTask"
  • 无论何时调用此任务,我的应用程序都会转到其ockService"并调用.tryAcquireLock("expensiveTaskId", 10, TimeUnit.Minutes)并获取一个布尔值,如果它获得了锁定。
  • 如果获得锁定则启动任务
  • 如果它没有锁定它并没有显示用户"你必须更耐心"

在分布式设置中,lockService的实现使用redis(和Redisson库)分布式锁(这已经很好用了)! 要在分布式和独立模式之间进行非常简单的切换,我只希望实现lockService并不依赖于任何外部服务。因此,我只需要一个支持超时的Lock实现。有了这个,我可以简单地在lockservice中有一个ConcurrentHashMap,它将lock-id映射到这些锁实例。

为什么不简单地使用将lock-id映射到时间对象的Map:因为我还需要阻止其他线程重新锁定(延长生命周期)另一个线程获取的锁。

4 个答案:

答案 0 :(得分:2)

您的描述有点含糊不清,因为您正在谈论锁,但您实际上并未锁定资源(或未提供示例)。我觉得你的问题与日程安排有关。

由于您已经使用过Spring,因此您可以查看其调度选项。最新版本允许您使用@Scheduled注释来触发它。 @EnableScheduling启动后台任务执行程序。您可以将它与Spring配置文件组合在一起,以确保仅在传递配置文件时启用它们,例如作为JVM参数。

从文档复制:

package hello;

import java.text.SimpleDateFormat;
import java.util.Date;

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class ScheduledTasks {

    private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");

    @Scheduled(fixedRate = 5000)
    public void reportCurrentTime() {
        System.out.println("The time is now " + dateFormat.format(new Date()));
    }
}

并启用:

package hello;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication
@EnableScheduling
public class Application {

    public static void main(String[] args) throws Exception {
        SpringApplication.run(Application.class);
    }
}

这里有一个快速指南:

Spring Docs

服务代码(您希望使用枚举器,为清晰起见使用字符串):

import org.apache.commons.collections4.map.PassiveExpiringMap;

public class StandAloneLockService {
    private Map ordinaryLocks;
    private Map expiringLocks;

    public StandAloneLockService() {
        this.ordinaryLocks = new HashMap<String, Long>();
        this.expiringLocks = new PassiveExpiringMap<String, Long>(2L,
                TimeUnit.MINUTES);
    }

    public synchronized boolean accquireLock(String task) {
        if (ordinaryLocks.containsKey("task")
                || expiringLocks.containsKey("task")) {
            return false;
        } else {
            return handle("task");
        }
    }

    private boolean handle(String jdk7) {
        switch (jdk7) { // logic 
        }
    }
    private void releaseLock(String task) {
        switch (task) { // logic 
        }
    }
}

答案 1 :(得分:0)

Object类中有一个方法:public final void wait(long timeout)。见http://docs.oracle.com/javase/7/docs/api/java/lang/Object.html#wait(long)

您只能在同步块中调用它。

作为示例(来自javadoc):

synchronized (obj) {
         while (<condition does not hold>)
             obj.wait(timeout);
         ... // Perform action appropriate to condition
     }

答案 2 :(得分:0)

如果有人有兴趣,这是我为“无集群”模式提出的LockService实现:

{{1}}

因此,没有使用真正的锁定,锁定通过服务发生,TimeoutLock对象只是跟踪拥有的线程ID和超时。 为它做了一些测试,到目前为止一切看起来都不错。

答案 3 :(得分:0)

您可以尝试在ReentrantLock中等待调用超时,例如:

public class MessageUtil {
  private static final Lock lock = new ReentrantLock();

  public enum Conditions {
     BAR_INIT(lock.newCondition()),
     TEST_DELAY(lock.newCondition());

     Condition condition;

     private Conditions(Condition condition) {
         this.condition = condition;
     }
  }

  public static void await(Conditions condition, int timeout) throws Interrupted Exception {
     lock.lock();
     condition.condition.await(timeout, TimeUnit.SECONDS);
     lock.unlock();
  }

  public static void signalAll(Conditions condtition) {
     lock.lock();
     condition.condition.signalAll();
     lock.unlock();
  }
}

此实用程序类允许您使用await方法等待某个条件,比如等待Bar类完成初始化或等待测试中的某个步骤然后使用signalAll方法结束等待条件并恢复正常运作。