项目中需要使用分布式锁,且不止一处使用,所以提取为公共,具体代码实现如下:
工具类:
import java. util. HashMap ;
import java. util. Map ;
public class ThreadLocalUtil {
private static ThreadLocal tlContext = new ThreadLocal ( ) ;
public static void put ( Object key, Object value) {
Map m = ( Map ) tlContext. get ( ) ;
if ( m == null ) {
m = new HashMap ( ) ;
tlContext. set ( m) ;
}
m. put ( key, value) ;
}
public static Object get ( Object key) {
Map m = ( Map ) tlContext. get ( ) ;
if ( m == null ) {
return null ;
}
return m. get ( key) ;
}
public static void clear ( Object key) {
Map m = ( Map ) tlContext. get ( ) ;
if ( m == null ) {
return ;
}
m. remove ( key) ;
}
}
异常:
public class RedisLockException extends RuntimeException {
public RedisLockException ( String message) {
super ( message) ;
}
}
注解:
import java. lang. annotation. * ;
@Target ( {
ElementType . METHOD} )
@Retention ( RetentionPolicy . RUNTIME)
@Documented
@Inherited
public @interface RedisLock {
String key ( ) default "" ;
long interval ( ) default 100L ;
long expireTime ( ) default 10 * 1000L ;
long timeout ( ) default 5 * 1000L ;
}
实现:
import com. base. core. util. ThreadLocalUtil ;
import com. base. redis. err. RedisLockException ;
import lombok. AllArgsConstructor ;
import lombok. extern. slf4j. Slf4j ;
import org. aspectj. lang. ProceedingJoinPoint ;
import org. aspectj. lang. annotation. Around ;
import org. aspectj. lang. annotation. Aspect ;
import org. aspectj. lang. reflect. MethodSignature ;
import org. springframework. core. DefaultParameterNameDiscoverer ;
import org. springframework. expression. EvaluationContext ;
import org. springframework. expression. Expression ;
import org. springframework. expression. spel. standard. SpelExpressionParser ;
import org. springframework. expression. spel. support. StandardEvaluationContext ;
import java. lang. reflect. Method ;
import java. util. UUID;
@Aspect
@Slf4j
@AllArgsConstructor
public class RedisLockAspect {
IRedisLockService redisService;
SpelExpressionParser spelExpressionParser;
DefaultParameterNameDiscoverer nameDiscoverer;
private final static String KEY_PREFIX = "redisLock:" ;
@Around ( value = "@annotation(RedisLock)" )
public Object redisLockAop ( ProceedingJoinPoint joinPoint) {
RedisLock lock = ( ( MethodSignature ) joinPoint. getSignature ( ) ) . getMethod ( ) . getAnnotation ( RedisLock . class ) ;
String uuid = UUID. randomUUID ( ) . toString ( ) ;
String key = getKey ( joinPoint, lock. key ( ) ) ;
log. info ( Thread . currentThread ( ) . getName ( ) + ":获取key:" + key) ;
if ( ThreadLocalUtil . get ( key) != null ) {
return execute ( key, uuid, joinPoint) ;
}
if ( redisService. tryLock ( key, uuid, lock. expireTime ( ) , lock. timeout ( ) , lock. interval ( ) ) ) {
ThreadLocalUtil . put ( key, "" ) ;
return execute ( key, uuid, joinPoint) ;
}
throw new RedisLockException ( "redis分布式锁异常" ) ;
}
private Object execute ( String key, String uuid, ProceedingJoinPoint joinPoint) {
try {
return joinPoint. proceed ( ) ;
} catch ( Throwable throwable) {
log. error ( "redisAop方法执行异常:{}" , throwable. toString ( ) ) ;
} finally {
ThreadLocalUtil . clear ( key) ;
redisService. unLock ( key, uuid) ;
}
return null ;
}
public String getKey ( ProceedingJoinPoint joinPoint, String key) {
String className = joinPoint. getSignature ( ) . getDeclaringTypeName ( ) ;
String methodName = joinPoint. getSignature ( ) . getName ( ) ;
if ( "" . equals ( key) ) {
return className + methodName;
}
String suffixKey = generateKeyBySpEL ( key, joinPoint) ;
return KEY_PREFIX + className + ":" + methodName + ":" + suffixKey;
}
privateString generateKeyBySpEL ( String expressionString, ProceedingJoinPoint joinPoint) {
MethodSignature methodSignature = ( MethodSignature ) joinPoint. getSignature ( ) ;
Method method = methodSignature. getMethod ( ) ;
String [ ] paramNames = nameDiscoverer. getParameterNames ( method) ;
Expression expression = spelExpressionParser. parseExpression ( expressionString) ;
EvaluationContext context = new StandardEvaluationContext ( ) ;
Object [ ] args = joinPoint. getArgs ( ) ;
for ( int i = 0 ; i < args. length; i++ ) {
context. setVariable ( paramNames[ i] , args[ i] ) ;
}
return expression. getValue ( context) . toString ( ) ;
}
}
Redis锁
public interface IRedisLockService {
boolean tryLock ( String key, String value, long expireTime, long timeout, long interval) ;
void unLock ( String key, String value) ;
}
Redis锁实现
import lombok. AllArgsConstructor ;
import lombok. extern. slf4j. Slf4j ;
import org. springframework. data. redis. core. RedisTemplate ;
import java. util. concurrent. TimeUnit ;
@Slf4j
@AllArgsConstructor
public class RedisLockService implements IRedisLockService {
RedisTemplate < String , Object > redisTemplate;
@Override
public boolean tryLock ( String key, String value, long expireTime, long timeout, long interval) {
if ( interval <= 0 ) {
interval = 30L ;
}
if ( timeout > 0 ) {
long begin = System . currentTimeMillis ( ) ;
while ( System . currentTimeMillis ( ) - begin < timeout) {
if ( Boolean . TRUE. equals ( redisTemplate. opsForValue ( ) . setIfAbsent ( key, value, expireTime, TimeUnit . MILLISECONDS) ) ) {
log. info ( Thread . currentThread ( ) . getName ( ) + ":" + key + ":上锁" ) ;
return true ;
}
synchronized ( Thread . currentThread ( ) ) {
log. info ( Thread . currentThread ( ) . getName ( ) + ":等待" ) ;
try {
Thread . currentThread ( ) . wait ( interval) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
}
}
}
return false ;
} else {
Boolean is = redisTemplate. opsForValue ( ) . setIfAbsent ( key, value, expireTime, TimeUnit . MILLISECONDS) ;
return Boolean . TRUE. equals ( is) ;
}
}
@Override
public void unLock ( String key, String value) {
Boolean delete = redisTemplate. delete ( key) ;
if ( null != delete && delete) {
log. info ( Thread . currentThread ( ) . getName ( ) + ":" + key + ":解锁成功" ) ;
}
}
}