[英]Is there a Java Lock implementation with timeouts
我有一个可以在集群环境中运行的spring应用程序。 在那种环境中,我将Redis(和Redisson)用作分布式锁定服务。
许多锁用于例如保护某些任务,这些任务一次只能运行一次,或者只能每X秒启动一次。
该应用程序还能够在独立模式下运行(无需redis)。
但是对于这种情况,我需要锁服务的不同实现。 我认为这将非常简单,因为我只需要在本地创建具有一定超时时间的Lock实例即可(例如,“最多2分钟只能运行一次动作”)。 但是,当环顾四周时,我找不到支持设置超时设置的Java Lock接口的任何实现(这样,在该时间之后它会自动解锁)。
是否有这样的事情,或者有一种我自己缺少的实现代码的极其简单的方法(就代码行而言)?
该锁隐含的行为应如何:
编辑:似乎一个具体的例子可以帮助理解我在寻找什么:
.tryAcquireLock("expensiveTaskId", 10, TimeUnit.Minutes)
并在是否获得锁定的情况下返回一个布尔值。 在分布式设置中, lockService
的实现使用redis(和Redisson库)分布式锁(这已经很好用了!)! 要在分布式和独立模式之间进行非常简单的切换,我只想实现不依赖任何外部服务的lockService
实现。 因此,我只需要实现支持超时的Lock即可。 这样,我可以在锁服务内部简单地拥有一个ConcurrentHashMap,它将锁ID映射到这些锁实例。
为什么不简单地使用将锁ID映射到时间对象的Map:因为我还需要防止其他线程重新锁定(延长生存期)被另一个线程获取的锁。
在谈论锁时,您的描述有些含糊,但实际上并没有锁定资源(或未提供示例)。 我觉得您的问题与安排有关。
由于您已经使用过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
}
}
}
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
}
如果有人感兴趣,这是我为“无集群”模式想到的LockService实现:
import com.google.common.collect.Maps;
import org.slf4j.Logger;
import java.lang.management.ThreadInfo;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalUnit;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import static org.slf4j.LoggerFactory.getLogger;
public class LocalLockServiceImpl implements LockService {
private static final Logger LOG = getLogger(ClusterLockServiceLocalImpl.class);
private ConcurrentMap<String, TimeoutLock> lockMap = Maps.newConcurrentMap();
private Object sync = new Object();
@Override
public boolean tryLockOrRelock(String lockName, long lockTimeMillis) {
synchronized (sync) {
TimeoutLock lock = lockMap.getOrDefault(lockName, new TimeoutLock());
lockMap.put(lockName, lock);
if (!lock.isExpired()) {
if (!lock.isHeldByCurrentThread()) {
LOG.debug("cannot lock " + lockName + " because it is held by a different thread");
return false;
}
}
lock.setExpiry(lockTimeMillis);
return true;
}
}
@Override
public void unlock(String lockName) {
synchronized (sync) {
TimeoutLock lock = lockMap.getOrDefault(lockName, null);
if (lock != null && lock.isHeldByCurrentThread()) {
lockMap.remove(lockName);
}
}
}
private static class TimeoutLock {
private LocalDateTime expiresAt;
private long threadId;
public TimeoutLock() {
expiresAt = LocalDateTime.now();
threadId = Thread.currentThread().getId();
}
public void setExpiry(long millisFromNow) {
expiresAt = LocalDateTime.now().plus(millisFromNow, ChronoUnit.MILLIS);
}
public boolean isHeldByCurrentThread() {
return threadId == Thread.currentThread().getId();
}
public boolean isExpired() {
return expiresAt.isBefore(LocalDateTime.now());
}
}
}
因此,没有使用真正的锁,而是通过服务进行锁定,而TimeoutLock对象只是跟踪拥有的线程ID和超时。 为此编写了一些测试,到目前为止一切都看起来不错。
您可以尝试将超时放入ReentrantLock的await调用中,例如:
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方法终止等待条件并恢复正常操作。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.