互联网面试题二——分布式锁

4 篇文章 0 订阅
1 篇文章 0 订阅

项目场景:

分布式环境下,基于java单机的syscronized或者lock相关的锁不能生效。


解决方案

  • redis分布式锁(基于setnx封装,或者使用redisson实现)(并发比zk高)
    加锁: set k v nx ex [time-1s] + watch dog
    释放锁:delete k
    死锁情况:
    - 加锁且没有释放锁(需要加释放锁操作,如delete key)
    - 加锁后,程序还没有执行到释放锁操作代码,程序已经挂掉。(加锁时设置过期机制)

  • 基于zookeeper,顺序临时节点

  • 基于数据库主键或者唯一索引实现

具体实现

redis分布式锁

基于jedis操作setnx ex命令

ps.代码不完善,仅供参考
代码:

/**
     *  添加string数据并加锁
     *
     * @param key
     * @param value
     */
    public static String stringSetNxEx(String key, String value,int expireTime) {
        Jedis jedis = null;
        boolean isBroken = false;
        String lastVal = null;
        try {
            jedis = getJedis();
            jedis.select(0);
            jedis.set(key,value,"NX","EX",expireTime);
            lastVal = jedis.get(key);
        } catch (Exception e) {
            e.printStackTrace();
            isBroken = true;
        } finally {
            closeResource(jedis, isBroken);
        }
        return lastVal;
    }

测试代码:

    public static void main(String[] args) throws InterruptedException {
        String key = "businness:id:lock";
        String value = String.valueOf(RandomUtils.nextInt());
        // 加锁
        String valueInRedis = stringSetNxEx(key,value,60);
        // 判断是否是当前线程加锁
        System.out.println(valueInRedis);
        if(valueInRedis.equals(value)){
            // todo 业务逻辑
            Thread.sleep(1000);
            // 释放锁
            delKey(key);
            System.out.println("已释放锁");
        }else{
            System.out.println("加锁失败");
        }
    }

运行结果:

  1. redis未加锁情况:
    在这里插入图片描述
  2. 已加锁情况(手动操作redis加锁)
    在这里插入图片描述
    在这里插入图片描述

基于redisson

redisson中提供了丰富的api 比如readlock writelock redlock等,这里只用简单的lock尝试
1.引入依赖

		 <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.16.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

2.添加配置bean

import org.redisson.Redisson;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RedissionConfig {

    @Bean
    public Redisson redisson(){
        // 单机模式配置
        Config config = new Config();
        config.useSingleServer().setAddress("redis://127.0.0.1:6379").setDatabase(1);
        return (Redisson)Redisson.create(config);
    }
}

3.编写业务控制器demo

import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@RestController
@Slf4j
public class RedissonController {

    @Autowired
    private Redisson redisson;

    @Resource
    private StringRedisTemplate template;

    @GetMapping(value = "/lock/demo")
    public String demo(){
        // 0.标识号
        String clientID = UUID.randomUUID().toString();
        // 1.这个相当于一把锁,控制只能一个人来
        String lockKey = "bussiness:count:lock";
        // 创建锁对象
        RLock lock = redisson.getLock(lockKey);

        try {
            lock.lock(30, TimeUnit.SECONDS);
            log.info("redis 加锁");
            // 获取redis中的数量 并扣减
            synchronized(this){
                int count = Integer.parseInt(template.opsForValue().get("count"));
                if (count > 0){
                    int realCount = count - 1;
                    template.opsForValue().set("count", realCount + "");
                    log.info("count扣减成功,剩余 {}",realCount);
                }else {
                    log.error("count扣减失败,数量不足",count);
                }
            }
        }finally {
            // 释放锁
            lock.unlock();
            log.info("redis 释放锁");
        }
        return null;
    }
}

运行结果:
1.redis中设置count数量

在这里插入图片描述
2.调用api
http://localhost:8080/lock/demo

在这里插入图片描述
3.超出count数量调用结果
在这里插入图片描述
在这里插入图片描述
4.添加延时,模拟业务代码,查看锁的key
在这里插入图片描述

zookeeper实现分布式锁

TODO

Mysql实现分布式锁

TODO

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

叁达补溜

求个三连

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值