package com.thgy.bc.common.lock;
import lombok.*;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.boot.CommandLineRunner;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.*;
/**
* RowLevelLock
* Example:
* <pre>
* try{
* lock(id)
* } finally {
* unlock(id)
* }
* </pre>
*
* @author Ryan
* @version 1.0
* @date 2020/05/26
*/
@Component
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class RowLevelLock<K> {
private static final int DELAY_IN_SECOND = 60;
private final Map<K, Semaphore> map = new ConcurrentHashMap<>();
private final DelayQueue<DelayTask> queue = new DelayQueue();
/**
* lock
*
* @param key
*/
public void lock(@NonNull K key) {
map.computeIfAbsent(key, (k) -> new Semaphore(1))
.acquireUninterruptibly();
queue.offer(new DelayTask(
DateUtils.addSeconds(new Date(), DELAY_IN_SECOND),
key));
}
/**
* unlock
*
* @param key
*/
public void unlock(@NonNull K key) {
map.computeIfPresent(key, (k, v) -> {
if (!v.hasQueuedThreads()) {
map.remove(key);
queue.remove(new DelayTask(key));
}
v.release();
return v;
});
}
/**
* try with resource syntax
*
* @param key
* @return
*/
public AutoCloseable tryLock(@NonNull K key) {
lock(key);
return () -> {
unlock(key);
};
}
@Getter
@RequiredArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(of = "key")
private class DelayTask implements Delayed, CommandLineRunner {
private Date delayTime;
@NonNull
private K key;
@Override
public long getDelay(TimeUnit unit) {
return delayTime.getTime() - System.currentTimeMillis();
}
@Override
public int compareTo(Delayed o) {
return delayTime.compareTo(((DelayTask) o).getDelayTime());
}
@Async
@Override
public void run(String... args) throws Exception {
try {
while (true) {
unlock(queue.take().getKey());
}
} catch (InterruptedException e) {
e.printStackTrace();
//retry
run();
}
}
}
}
版权声明:本文为qq_44309610原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。