原理很简单,直接上代码了
同步锁
import org.springframework.data.redis.core.StringRedisTemplate;
import java.util.concurrent.TimeUnit;
/**
* 同步锁
* 请尽量避免直接构造该类,改用SyncLockFactory创建
*
* @author yangguo
*/
public class SyncLock {
/**
* Redis key
*/
private String key;
/**
* stringRedisTemplate
*/
private StringRedisTemplate stringRedisTemplate;
/**
* Redis TTL/秒
*/
private Long expire;
/**
* safetyTime 安全时间/秒
*/
private Long safetyTime;
private Long waitMillisPer = 10L;
private String value(){
return Thread.currentThread().getId() + "-" + Thread.currentThread().getName();
}
public SyncLock(String key, StringRedisTemplate stringRedisTemplate, Long expire, Long safetyTime) {
this.key = key;
this.stringRedisTemplate = stringRedisTemplate;
this.expire = expire;
this.safetyTime = safetyTime;
}
/**
* 尝试获取锁(立即返回)
*
* @return 是否获取成功
*/
public Boolean tryLock() {
Boolean locked = stringRedisTemplate.opsForValue().setIfAbsent(key, value());
if (locked) {
stringRedisTemplate.expire(key, expire, TimeUnit.SECONDS);
}
return locked;
}
/**
* 尝试获取锁,并至多等待timeout时长
*
* @param timeout 超时时长
* @param unit 时间单位
* @return 是否获取成功
*/
public Boolean tryLock(Long timeout, TimeUnit unit) {
Long waitMax = unit.toMillis(timeout);
Long waitAlready = 0L;
while (stringRedisTemplate.opsForValue().setIfAbsent(key, value()) != true && waitAlready < waitMax) {
try {
Thread.sleep(waitMillisPer);
} catch (InterruptedException e) {
e.printStackTrace();
}
waitAlready += waitMillisPer;
}
if (waitAlready < waitMax) {
stringRedisTemplate.expire(key, expire, TimeUnit.SECONDS);
return true;
}
return false;
}
/**
* 获取锁
*/
public void lock() {
Long waitMax = TimeUnit.SECONDS.toMillis(safetyTime);
Long waitAlready = 0L;
while (stringRedisTemplate.opsForValue().setIfAbsent(key, value()) != true && waitAlready < waitMax) {
try {
Thread.sleep(waitMillisPer);
} catch (InterruptedException e) {
e.printStackTrace();
}
waitAlready += waitMillisPer;
}
stringRedisTemplate.opsForValue().set(key, value(), expire, TimeUnit.SECONDS);
}
/**
* 释放锁
*/
public void unLock(){
if(stringRedisTemplate.opsForValue().get(key).equals(value())){
stringRedisTemplate.delete(key);
}
}
}
同步锁注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 同步锁注解
*
* @author yangguo
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SyncLockable {
String key();
/**
* TTL/秒,默认10秒
* @return
*/
long expire() default 10L;
}
同步锁注解处理
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* 同步锁注解处理
*
* @author yangguo
*/
@Aspect
@Component
public class SyncLockHandle {
@Autowired
private SyncLockFactory syncLockFactory;
/**
* 在方法上执行同步锁
*/
@Around("@annotation(syncLockable)")
public Object syncLock(ProceedingJoinPoint jp, SyncLockable syncLockable) {
SyncLock lock = syncLockFactory.build(syncLockable.key(), syncLockable.expire(), syncLockable.expire() * 5);
try {
lock.lock();
return jp.proceed();
} catch (Throwable e) {
e.printStackTrace();
} finally {
lock.unLock();
}
return null;
}
}
SyncLock同步锁工厂类
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* SyncLock同步锁工厂类
*
* @author yangguo
*/
@Component
public class SyncLockFactory {
@Autowired
private StringRedisTemplate stringRedisTemplate;
private ConcurrentMap<String,SyncLock> syncLockMap = new ConcurrentHashMap();
public synchronized SyncLock build(String key, Long expire, Long safetyTime) {
if (!syncLockMap.containsKey(key)) {
syncLockMap.put(key, new SyncLock(key, stringRedisTemplate, expire, safetyTime));
}
return syncLockMap.get(key);
}
}
使用方法就是直接在需要同步的方法上加注解,比如
@SyncLockable(key="sync1",100)
有什么问题可以在评论区讨论