不加锁问题
例如:我们在调用操作redis接口,获取key减去1在set进去。这样的操作,再遇到高并发
时,大家get的数据都一样,都减去1,再set进去,这样数据肯定是错的。
@Test
void contextLoads() {
Long num = (Long) redisUtil.get("num");
num = num - 1;
redisUtil.set("num",num);
}
使用synchronized同步锁或者redis加锁都会有很多问题
使用redisson分布式锁
1、配置
1、引入依赖
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.16.1</version>
</dependency>
2、配置
@Configuration
public class RedissonConfig {
@Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.port}")
private String port;
@Value("${spring.redis.password}")
private String password;
@Bean
public RedissonClient redissonClient() {
Config config = new Config();
// config.useClusterServers().addNodeAddress("redis://" + host + ":" + port); // 分片集群方式
SingleServerConfig server = config.useSingleServer();
config.setLockWatchdogTimeout(5 * 1000L);
server.setAddress("redis://" + host + ":" + port);
server.setPassword(password);
RedissonClient redissonClient = Redisson.create(config);
return redissonClient;
}
}
2、正常加锁
@Autowired
Redisson redisson;
RLock lock = redisson.getLock("lockKey"); //获取锁
lock.lock(); //加锁,注射性等待。默认加的锁的时间是30s。
// 1)、锁自动续期,如果业务时间超长,运行期间自动给锁续上新的30s.
// 2)、加锁的业务只要运行完成,就不会给当前锁续期,即使不手动解锁,锁默认在30s以后删除
// 加锁以后10秒钟自动解锁
// 无需调用unlock方法手动解锁
// lock.lock(10, TimeUnit.SECONDS);
// 尝试加锁,最多等待100秒,上锁以后10秒自动解锁
// boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS);
try {
// redis操作.
} finally {
lock.unlock(); // 释放锁
}
3、读写锁
写锁没完成,就读取不到数据。保证能读到最新数据。
@Test
public void write(){
// 获取读写锁
RReadWriteLock lock = redisson.getReadWriteLock("rw-lock");
RLock rLock = lock.writeLock();
try {
rLock.lock(); // 给写加锁,写没完成,读就读不出来
redisUtil.set("num",1);
}finally {
// 释放锁
rLock.unlock();
}
}
@Test
public void read(){
// 获取读写锁
RReadWriteLock lock = redisson.getReadWriteLock("rw-lock");
RLock rLock = lock.readLock();
rLock.lock(); // 加读锁,写锁没完成,就读不了
try {
redisUtil.get("num");
}finally {
// 释放锁
rLock.unlock();
}
}
4、闭锁
调用次数为我们设置的次数后就锁住。
@Test // 闭锁,设置次数和锁
public void lockDoor() throws InterruptedException {
RCountDownLatch door = redisson.getCountDownLatch("door");
door.trySetCount(5); // 5次后就锁住
door.await();
}
@Test // 闭锁 调一次,次数就减1,等到上面设置的5次后,就锁住
public void door() throws InterruptedException {
RCountDownLatch door = redisson.getCountDownLatch("door");
door.countDown();
}
5、信号量
信号量占用与释放:如设置停车场有3个车位,每次占用一个信号,等到占用到了3个,就会等待(或者可以返回false),这时再释放一个信号量,才能占用得到。
@Test
public void setparkCount() {
redisUtil.set("park",3); // 设置这个停车场有三个车位
}
@Test // 占用一个,如果3个都占用了,就锁住,一直等待
public void park() throws InterruptedException {
RSemaphore park = redisson.getSemaphore("park");
park.acquire(); //占用一个信号,获取一个值(如:占用一个车位),堵塞性等待
// 或者
// boolean b = park.tryAcquire();// 占用一个信号,如果车位位,返回false
}
@Test // 释放一个
public void go() throws InterruptedException {
RSemaphore park = redisson.getSemaphore("park");
park.release(); //释放一个信号(如: 释放一个车位)
}