说完 Redis 分布式锁,面试官对我竖起大拇指,linux驱动面试问题

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024b (备注Java)
img

正文

说完 Redis 分布式锁,面试官对我竖起大拇指

可以看出,当把keylock的值设置为"Java"后,再设置成别的值就会失败,看上去很简单,也好像独占了锁,但有个致命的问题,就是key没有过期时间,这样一来,除非手动删除 key 或者获取锁后设置过期时间,不然其他线程永远拿不到锁。

既然这样,我们给 key 加个过期时间总可以吧,直接让线程获取锁的时候执行两步操作:

SETNX Key 1

EXPIRE Key Seconds

这个方案也有问题,因为获取锁和设置过期时间分成两步了,不是原子性操作,有可能获取锁成功但设置时间失败,那样不就白干了吗。

不过也不用急,这种事情 Redis 官方早为我们考虑到了,所以就引出了下面这个命令

2、SETEX,用法SETEX key seconds value

将值 value 关联到 key ,并将 key 的生存时间设为 seconds (以秒为单位)。如果 key 已经存在,SETEX 命令将覆写旧值。

这个命令类似于以下两个命令:

SET key value

EXPIRE key seconds  # 设置生存时间

这两步动作是原子性的,会在同一时间完成。

说完 Redis 分布式锁,面试官对我竖起大拇指

3、PSETEX ,用法PSETEX key milliseconds value

这个命令和SETEX命令相似,但它以毫秒为单位设置 key 的生存时间,而不是像SETEX命令那样,以秒为单位。

不过,从 Redis 2.6.12 版本开始,SET命令可以通过参数来实现和SETNX、SETEX、PSETEX 三个命令的效果。

就比如这条命令

SET key value NX EX seconds

加上 NX、EX 参数后,效果就相当于 SETEX,这也是 Redis 获取锁写法里面最常见的。

怎么释放锁

=====

释放锁的命令就简单了,直接删除 key 就行,但我们前面说了,因为分布式锁必须由锁的持有者自己释放,所以我们必须先确保当前释放锁的线程是持有者,没问题了再删除,这样一来,就变成两个步骤了,似乎又违背了原子性了,怎么办呢?

不慌,我们可以用 lua 脚本把两步操作做拼装,就好像这样:

if Redis.call(“get”,KEYS[1]) == ARGV[1]

then

return Redis.call(“del”,KEYS[1])

else

return 0

end

KEYS[1]是当前 key 的名称,ARGV[1]可以是当前线程的 ID(或者其他不固定的值,能识别所属线程即可),这样就可以防止持有过期锁的线程,或者其他线程误删现有锁的情况出现。

代码实现

====

知道了原理后,我们就可以手写代码来实现 Redis 分布式锁的功能了,因为本文的目的主要是为了讲解原理,不是为了教大家怎么写分布式锁,所以我就用伪代码实现了。

首先是 Redis 锁的工具类,包含了加锁和解锁的基础方法:

public class RedisLockUtil {

private String LOCK_KEY = “redis_lock”;

// key的持有时间,5ms

private long EXPIRE_TIME = 5;

// 等待超时时间,1s

private long TIME_OUT = 1000;

// Redis命令参数,相当于nx和px的命令合集

private SetParams params = SetParams.setParams().nx().px(EXPIRE_TIME);

// Redis连接池,连的是本地的Redis客户端

JedisPool jedisPool = new JedisPool(“127.0.0.1”, 6379);

/**

* 加锁

* @param id

*            线程的id,或者其他可识别当前线程且不重复的字段

* @return

*/

public boolean lock(String id) {

Long start = System.currentTimeMillis();

Jedis jedis = jedisPool.getResource();

try {

for (;😉 {

// SET命令返回OK ,则证明获取锁成功

String lock = jedis.set(LOCK_KEY, id, params);

if (“OK”.equals(lock)) {

return true;

}

// 否则循环等待,在TIME_OUT时间内仍未获取到锁,则获取失败

long l = System.currentTimeMillis() - start;

if (l >= TIME_OUT) {

return false;

}

try {

// 休眠一会,不然反复执行循环会一直失败

Thread.sleep(100);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

} finally {

jedis.close();

}

}

/**

* 解锁

* @param id

*            线程的id,或者其他可识别当前线程且不重复的字段

* @return

*/

public boolean unlock(String id) {

Jedis jedis = jedisPool.getResource();

// 删除key的lua脚本

String script = “if Redis.call(‘get’,KEYS[1]) == ARGV[1] then” + "   return Redis.call(‘del’,KEYS[1]) " + “else”

+ "   return 0 " + “end”;

try {

String result =

jedis.eval(script, Collections.singletonList(LOCK_KEY), Collections.singletonList(id)).toString();

return “1”.equals(result);

} finally {

jedis.close();

}

}

}

具体的代码作用注释已经写得很清楚了,然后我们就可以写一个 demo 类来测试一下效果:

public class RedisLockTest {

private static RedisLockUtil demo = new RedisLockUtil();

private static Integer NUM = 101;

public static void main(String[] args) {

for (int i = 0; i < 100; i++) {

new Thread(() -> {

String id = Thread.currentThread().getId() + “”;

boolean isLock = demo.lock(id);

try {

// 拿到锁的话,就对共享参数减一

if (isLock) {

NUM–;

System.out.println(NUM);

}

} finally {

// 释放锁一定要注意放在finally

总结:绘上一张Kakfa架构思维大纲脑图(xmind)

image

其实关于Kafka,能问的问题实在是太多了,扒了几天,最终筛选出44问:基础篇17问、进阶篇15问、高级篇12问,个个直戳痛点,不知道如果你不着急看答案,又能答出几个呢?

若是对Kafka的知识还回忆不起来,不妨先看我手绘的知识总结脑图(xmind不能上传,文章里用的是图片版)进行整体架构的梳理

梳理了知识,刷完了面试,如若你还想进一步的深入学习解读kafka以及源码,那么接下来的这份《手写“kafka”》将会是个不错的选择。

  • Kafka入门

  • 为什么选择Kafka

  • Kafka的安装、管理和配置

  • Kafka的集群

  • 第一个Kafka程序

  • Kafka的生产者

  • Kafka的消费者

  • 深入理解Kafka

  • 可靠的数据传递

  • Spring和Kafka的整合

  • SpringBoot和Kafka的整合

  • Kafka实战之削峰填谷

  • 数据管道和流式处理(了解即可)

image

image

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
Boot和Kafka的整合

  • Kafka实战之削峰填谷

  • 数据管道和流式处理(了解即可)

[外链图片转存中…(img-yxOfLhlI-1713362268589)]

[外链图片转存中…(img-xZDGMyNY-1713362268590)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
[外链图片转存中…(img-MaEBxHHK-1713362268590)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值