分布式缓存(四)分布式锁 - Redisson

概述:

Redisson是一个在Redis的基础上实现的Java驻内存数据网格,它不仅提供了一系列的分布式的java常用对象,还提供了许多分布式服务,包括Map,List,Set,Lock.

测试使用:

<!-- Redisson分布式锁-->
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.12.0</version>
</dependency>
@Configuration
public class MyRedissonConfig {
    /** 所有对Redisson的使用都是通过RedissonClient对象 */
    // 注意:此处讲到@@Autowired之后注入的名字最好和redisson()方法的名字相同
    @Bean(destroyMethod = "shutdown")
    public RedissonClient redisson() throws IOException{
        // 1.创建配置
        Config config = new Config();
        // 2.使用单节点模式
        config.useSingleServer().setAddress("redis://192.168.56.10:6379");
        // 3.根据config创建出RedissonClient实例
        RedissonClient redissonClient = Redisson.create(config);
        return redissonClient;
    }
}
@Autowired
RedissonClient redissonClient;

@Test
public void redisson() {
    System.out.println(redissonClient);
}

1.可重入锁  lock锁

A{

  ...

  B{}

  ... 

}

可重入锁:

例如对A加了1号锁,B同时也想加1号锁,可重入锁就是,B看到A已经加了1好锁,B就直接拿来用,B可以直接执行,执行完成A释放锁

不可重入锁:

A持有1号锁,不可重入锁,B要等待A来释放1号锁,才能持有1号锁,这就形成了一个死锁,因为B永远等不到A释放1号锁,因为A还想等B执行完成才释放锁

所有的锁都应该设计为可重入锁,来避免死锁问题

@ResponseBody
@GetMapping("/hello")
public String hellp(){
    // 获取一把锁,只要锁的名字一样,就是同一把锁
    RLock lock = redissonClient.getLock("my-lock");
    // 加锁  这个是阻塞式等待
    lock.lock();
    try {
        System.out.println("加锁成功,执行业务..." + Thread.currentThread().getId());
        // 模拟业务执行30s
        Thread.sleep(30000);
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        // 解锁
        System.out.println("释放锁..." + Thread.currentThread().getId());
        lock.unlock();
    }
    return "hello";
}

结果:

加锁成功,执行业务...116

释放锁...116

加锁成功,执行业务...117

释放锁...117

假设finall中的解锁代码没有运行,redisson会不会出现死锁呢?

  1. 锁的自动续期:如果业务超长,运行期间自动给锁续上新的30s(redisson默认加的锁都是30s),不用担心业务时间长,锁自动过期被删掉

  2. 加锁的业务只要运行完成,就不会给当前锁续期,即使不手动解锁,锁也会默认在30s后自动删除

2.Redisson - 读写锁

读锁跟写锁成对存在,只要写锁存在,读锁就得等待; 并发读之间互不影响;但是只能有一个去写

/** 修改期间,写锁是一个排他锁(互斥锁),读锁是一个共享锁  写锁没释放读必须等待*/
@ResponseBody
@GetMapping("/write")
public String write(){
    RReadWriteLock lock = redissonClient.getReadWriteLock("rw-lock");
    String s = "";
    RLock writeLock = lock.writeLock();
    try {
        // 改数据的时候加写锁,读数据加读锁
        writeLock.lock();
        s = UUID.randomUUID().toString();
        // 模拟业务执行,要写30s
        Thread.sleep(30000);
        redisTemplate.opsForValue().set("writeValue",s);
    } catch (InterruptedException e) {
        e.printStackTrace();
    } finally {
        writeLock.unlock();
    }
    return s;
}

@ResponseBody
@GetMapping("/read")
public String read() {
    RReadWriteLock lock = redissonClient.getReadWriteLock("rw-lock");
    RLock readLock = lock.readLock();
    String s = "";
    // 加读锁
    readLock.lock();
    try {
        s = redisTemplate.opsForValue().get("writeValue");
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        readLock.unlock();
    }
    return s;
}

3.Redisson - 信号量

/** 信号量测试 */
@ResponseBody
@GetMapping("/park")
public String park() throws InterruptedException {
    RSemaphore park = redissonClient.getSemaphore("park");
    park.acquire();
    // tryAcquire会得到一个结果true或者false,不用继续等待
    // boolean b = park.tryAcquire();
    // 可用作限流
    return "ok";
}

@ResponseBody
@GetMapping("/gogo")
public String gogo() throws InterruptedException {
    RSemaphore park = redissonClient.getSemaphore("park");
    park.release();
    return "ok";
}

4.Redisson - 闭锁

/** 闭锁 */
@ResponseBody
@GetMapping("/lockdoor")
public String lockdoor() throws InterruptedException {
    RCountDownLatch door = redissonClient.getCountDownLatch("door");
    door.trySetCount(5);
    door.await(); // 等待闭锁完成
    return "放假了 ...";
}

@ResponseBody
@GetMapping("/gogogo/{id}")
public String gogogo(@PathVariable("id") Long id) throws InterruptedException {
    RCountDownLatch door = redissonClient.getCountDownLatch("door");
    door.countDown(); // 计数减一
    return id + "班的人都走了 ...";
}

5.Redisson - 缓存一致性解决

当修改数据库时, 缓存中的数据如何与数据库中保持一致

1 双写模式

2 失效模式

 

缓存数据一致性的解决:

1.缓存数据+过期时间,足够解决大部分业务对于缓存的要求

2.通过加锁保证并发读写,写写的时候按照顺序排好队,读读无所谓,所以适合使用读写锁

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值