我有一个可以在集群环境中运行的spring应用程序。 在那种环境中,我使用Redis(和Redisson)作为分布式锁服务。
许多锁用于例如保护某些任务,这些任务一次只能运行一次,或者每隔X秒只能启动一次。
该应用程序还能够以独立模式运行(无需redis)。
但是对于这种情况,我需要一个不同的lockservice实现。 我认为这将非常简单,因为我只需要在本地创建一个具有特定超时的Lock实例(例如,对于"仅运行动作最多2分钟")。 但是当我环顾四周时,我找不到任何支持设置锁定超时的Java Lock接口的实现(以便在此之后自动解锁)。
是否有这样的事情,或者是否有一种非常简单的(就代码行而言)我自己实现这一点的方式,我只是错过了?
锁定impl应如何表现:
编辑: 似乎一个具体的例子可以帮助理解我在寻找什么:
.tryAcquireLock("expensiveTaskId", 10, TimeUnit.Minutes)
并获取一个布尔值,如果它获得了锁定。在分布式设置中,lockService
的实现使用redis(和Redisson库)分布式锁(这已经很好用了)!
要在分布式和独立模式之间进行非常简单的切换,我只希望实现lockService
并不依赖于任何外部服务。因此,我只需要一个支持超时的Lock实现。有了这个,我可以简单地在lockservice中有一个ConcurrentHashMap,它将lock-id映射到这些锁实例。
为什么不简单地使用将lock-id映射到时间对象的Map:因为我还需要阻止其他线程重新锁定(延长生命周期)另一个线程获取的锁。
答案 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);
}
}
这里有一个快速指南:
服务代码(您希望使用枚举器,为清晰起见使用字符串):
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方法结束等待条件并恢复正常运作。