1、利用NX的原子性,多个线程并发时,只有一个线程可以设置成功。
2、设置成功即获得锁,可以继续执行业务。
3、如果出现异常,过了锁的有效期,锁自动释放。
加锁:
ET resource_name my_random_value NX PX 30000 :设置分布式锁。
resource_name :为锁的key。
my_random_value : 必须保证每一个线程随机值不同、用于释放锁的时候校验(使用UUID) 。
NX :key不存在时设置成功,存在时设置不成功 。
PX :自动失效时间,锁可以过期失效防止异常情况。
解锁:
释放锁采用redis的delete命令。
释放锁时校验之前设置的随机数,相同才能释放。
释放锁的LUA脚本。
pom.xm文件中引入如下依赖:
org.springframework.boot
spring-boot-starter-data-redis
application.yml配置
spring:
redis:
host: 192.168.0.218
port: 6379
password: 123456
实现setnx锁的方法 (AutoCloseable 实现自动释放锁的接口)
package com.example.distributelock.lock;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.RedisStringCommands;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.data.redis.core.types.Expiration;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
@Slf4j
public class RedisLock implements AutoCloseable {
private RedisTemplate redisTemplate;
private String key;
private String value;
//单位:秒
private int expireTime;
public RedisLock(RedisTemplate redisTemplate,String key,int expireTime){
this.redisTemplate = redisTemplate;
this.key = key;
this.expireTime=expireTime;
this.value = UUID.randomUUID().toString();
}
/**
* 获取分布式锁
* @return
*/
public boolean getLock(){
RedisCallback redisCallback = connection -> {
//设置NX
RedisStringCommands.SetOption setOption = RedisStringCommands.SetOption.ifAbsent();
//设置过期时间
Expiration expiration = Expiration.seconds(expireTime);
//序列化key
byte[] redisKey = redisTemplate.getKeySerializer().serialize(key);
//序列化value
byte[] redisValue = redisTemplate.getValueSerializer().serialize(value);
//执行setnx操作
Boolean result = connection.set(redisKey, redisValue, expiration, setOption);
return result;
};
//获取分布式锁
Boolean lock = (Boolean)redisTemplate.execute(redisCallback);
return lock;
}
public boolean unLock() {
String script = "if redis.call(\"get\",KEYS[1]) == ARGV[1] then\n" +
" return redis.call(\"del\",KEYS[1])\n" +
"else\n" +
" return 0\n" +
"end";
RedisScript redisScript = RedisScript.of(script,Boolean.class);
List keys = Arrays.asList(key);
Boolean result = (Boolean)redisTemplate.execute(redisScript, keys, value);
log.info("释放锁的结果:"+result);
return result;
}
@Override
public void close() throws Exception {
unLock();
}
}
controller实现类
package com.example.distributelock.controller;
import com.example.distributelock.lock.RedisLock;
import com.example.distributelock.lock.ZkLock;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@Slf4j
public class RedisLockController {
@Autowired
private RedisTemplate redisTemplate;
@RequestMapping("redisLock")
public String redisLock() {
log.info("我进入了方法!");
try (RedisLock redisLock = new RedisLock(redisTemplate, "redisKey", 30)) {
if (redisLock.getLock()) {
log.info("我进入了锁!!");
Thread.sleep(15000);
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
log.info("方法执行完成");
return "方法执行完成";
}
}
pom.xml引入如下依赖
org.redisson
redisson
3.11.2
创建redisson.xml 文件
启动类上需要导入配置文件
@SpringBootApplication
@ImportResource("classpath*:redisson.xml")
public class RedissonLockApplication {
public static void main(String[] args) {
SpringApplication.run(RedissonLockApplication.class, args);
}
}
controller实现
package com.hy.redissonlock.controller;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
@RestController
@Slf4j
public class RedissonLockController {
@Resource
private RedissonClient redisson;
@RequestMapping("redissonLock")
public String redissonLock() {
RLock rLock = redisson.getLock("order");
log.info("我进入了方法!!");
try {
rLock.lock(30, TimeUnit.SECONDS);
log.info("我获得了锁!!!");
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
log.info("我释放了锁!!");
rLock.unlock();
}
log.info("方法执行完成!!");
return "方法执行完成!!";
}
}
启动项目测试:
后台日志:
pom.xml引入如下依赖
org.redisson
redisson
3.11.2
测试用例:
package com.hy.redissonlock;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.concurrent.TimeUnit;
@RunWith(SpringRunner.class)
@SpringBootTest
@Slf4j
public class RedissonLockApplicationTests {
@Test
public void contextLoads() {
}
@Test
public void testRedissonLock() {
Config config = new Config();
config.useSingleServer().setAddress("redis://192.168.0.218:6379");
config.useSingleServer().setPassword("123456");
RedissonClient redisson = Redisson.create(config);
RLock rLock = redisson.getLock("order");
try {
rLock.lock(30, TimeUnit.SECONDS);
log.info("我获得了锁!!!");
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
log.info("我释放了锁!!");
rLock.unlock();
}
}
}
测试结果:
pom.xm 引入如下依赖
org.redisson
redisson-spring-boot-starter
3.12.0
application.yml 配置
spring:
redis:
host: 192.168.0.218
port: 6379
password: 123456
启动类:(需要将导入配置文件注释掉)
@SpringBootApplication
//@ImportResource("classpath*:redisson.xml")
public class RedissonLockApplication {
public static void main(String[] args) {
SpringApplication.run(RedissonLockApplication.class, args);
}
}
controller实现
package com.hy.redissonlock.controller;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
@RestController
@Slf4j
public class RedissonLockController {
@Resource
private RedissonClient redisson;
@RequestMapping("redissonLock")
public String redissonLock() {
RLock rLock = redisson.getLock("order");
log.info("我进入了方法!!");
try {
rLock.lock(30, TimeUnit.SECONDS);
log.info("我获得了锁!!!");
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
log.info("我释放了锁!!");
rLock.unlock();
}
log.info("方法执行完成!!");
return "方法执行完成!!";
}
}
测试
启动项目
后台日志