[英]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.