以下代码使用kotlin
实现:
RedisLockService
import java.time.Duration
interface RedisLockService {
/**
* 加锁
*
* @param key redis键值对的key
* @param timeout redis键值对的过期时间 毫秒为单位
* @return boolean 是否加锁成功
*/
fun lock(key: String, timeout: Duration): Boolean
/**
* 释放锁
*
* @param key 释放本请求对应的key
*/
fun unlock(key: String)
}
实现类:
import com.aegis.oa.common.service.RedisLockService
import org.slf4j.LoggerFactory
import org.springframework.data.redis.core.StringRedisTemplate
import org.springframework.data.redis.core.script.DefaultRedisScript
import org.springframework.stereotype.Component
import java.time.Duration
@Component
class RedisLockServiceImpl(private val redisTemplate: StringRedisTemplate) : RedisLockService {
/** 定义获取锁的lua脚本 */
private val unlockLua = DefaultRedisScript(
"if redis.call('get', KEYS[1]) == 'lock' then return redis.call('del',KEYS[1]) else return -1 end",
Long::class.java
)
companion object {
private const val LOCK_EXPIRED = -1L
private const val LOCK_SUCCESS = 1L
private val log = LoggerFactory.getLogger(RedisLockServiceImpl::class.java)
}
/**
* 加锁
*
* @param key redis键值对的key
* @param timeout redis键值对的过期时间 毫秒为单位
* @return boolean 是否加锁成功
*/
override fun lock(key: String, timeout: Duration): Boolean {
try { // 执行脚本
@Suppress("SpellCheckingInspection")
val lockLua = DefaultRedisScript(
"if redis.call('setnx', KEYS[1], 'lock') == 1 then return redis.call('pexpire', " +
"KEYS[1], KEYS[2]) else return 0 end",
Long::class.java
)
/* 存储本地变量 */
val result = redisTemplate.execute(
lockLua,
listOf(
key, timeout.toMillis().toString()
)
)
if (LOCK_SUCCESS == result) {
return true
}
} catch (e1: Exception) {
log.error(
String.format(
"REDIS加锁异常,key:%s,threadName:%s", key, Thread.currentThread().name
), e1
)
}
return false
}
/**
* 释放锁
*
* @param key 释放本请求对应的key
*/
override fun unlock(key: String) {
try { // 使用lua脚本删除redis中匹配value的key,可以避免由于方法执行时间过长而redis锁自动过期失效的时候误删其他线程的锁
val keys = listOf(key)
/* 如果这里抛异常,后续锁无法释放 */
val result: Long = redisTemplate.execute(unlockLua, keys)
if (LOCK_SUCCESS == result) {
log.info("解锁成功,key:{},threadName:{},结果:{}", key, Thread.currentThread().name, result)
return
}
if (LOCK_EXPIRED == result) { //
// 返回-1说明获取到的KEY值与requestId不一致或者KEY不存在,可能已经过期或被其他线程加锁
// 一般发生在key的过期时间短于业务处理时间,属于正常可接受情况
log.debug(
"解锁成功,key:{},threadName:{},结果:{}",
key,
Thread.currentThread().name,
result
)
}
} catch (e: Exception) {
log.error(
String.format(
"REDIS解锁异常,key:%s,threadName:%s", key, Thread.currentThread().name
), e
)
}
}
}