转载
redis分布式锁
随着业务发展的需要,原单体单机部署的系统被演化成分布式集群系统后,由于分布式系统多线程、多进程并且分布在不同机器上,这将使原单机部署情况下的并发控制锁策略失效,单纯的Java API并不能提供分布式锁的能力。为了解决这个问题就需要一种跨JVM的互斥机制来控制共享资源的访问,这就是分布式锁要解决的问题!
简单说就是保证在分布式环境下,解决这段代码同一时间只有一个线程访问,比如redis分布式锁防止分布式环境下的缓存击穿
1.setnx key(lock) value(uuid) + expire second (伪代码) 设置key(只有在 key 不存在时才能设置 key 的值)和过期时间
2.获取锁(能设置key值 即意味着拿到了锁) 如获取到了
3.查数据库 放入缓存
4.删除自己uuid的锁 return数据
5.没获取到锁的情况(没能设置key值)
6.线程休眠后自旋
另外这是不完善的,实际情况要加上lua脚本实现原子性 详情请看B站的尚硅谷redis视频课件
更方便的操作引入redisson实现分布式锁
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.10.4</version>
</dependency>
配置类
@Configuration
public class RedissonConfig {
@Bean
public RedissonClient redissonClient() {
Config config = new Config(); //redis://127.0.0.1:7181 //可以用"rediss://"来启用 SSL 连接
config.useSingleServer().setAddress("redis://127.0.0.1:6379");
RedissonClient redisson = Redisson.create(config);
return redisson;
}
}
实现方法
@RequestMapping("/hello7")
@ResponseBody
public int hello7(String username) throws Exception {
//获取锁 只要锁的名字一样 就是一把锁
RLock lock = redissonClient.getLock("123");
//加锁 别的线程无限的堵塞性等待 二选一
lock.lock();//锁的默认时间是30s后自动删除 业务时间长 redisson会对锁自动续期 不用指定时间
lock.lock(1000, TimeUnit.MILLISECONDS); //传入指定时间 那么到期后不会自动续期
try {
System.out.println("加锁成功,执行业务....." + Thread.currentThread().getId());
Thread.sleep(30000);
} catch (Exception e) {
e.printStackTrace();
} finally {
//解锁 假设业务执行中断电 解锁代码没有运行 redisson也会自动释放锁
lock.unlock();
}
return 1;
}
读写锁
/**
* 读锁 一个共享锁
* @return
*
* 读+读 相当于无锁
* 写+读 写完才能读
* 写+写 堵塞方式
* 读+写 读完才能写
*
* 只要有写的存在,都必须等待
*/
@RequestMapping("/hello8")
@ResponseBody
public String hello8() {
RReadWriteLock lock = redissonClient.getReadWriteLock("abc");
RLock rLock = lock.readLock();
try {
rLock.lock();
System.out.println("执行读业务代码");
Thread.sleep(30000);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
rLock.unlock();
}
return "1";
}
/**
* 写锁 一个排它锁
* @return
*/
@RequestMapping("/hello9")
@ResponseBody
public String hello9() {
RReadWriteLock lock = redissonClient.getReadWriteLock("abc");
RLock rLock = lock.writeLock();
try {
rLock.lock();
System.out.println("执行写业务代码");
Thread.sleep(30000);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
rLock.unlock();
}
return "1";
}
信号量 停车例子
闭锁 锁门例子