首页天道酬勤爱奇艺app登录,javaweb应用开发技术与案例教程

爱奇艺app登录,javaweb应用开发技术与案例教程

张世龙 05-05 23:43 43次浏览

开头提到Spring,大家一定不知道吧。 那是所有Java开发人员都绕不过去的坎。 Spring框架为基于java的企业APP应用程序提供了一系列解决方案,便于开发人员在框架的基础上快速进行业务开发。

我最近在研究大型制造商的面试,不管现在大小的互联网公司,Spring都是个绕圈子的话题,而且只是Spring的面试,从最初的官网入门到现在源代码的深入分析。 主要是四个系列:

1 .为什么要使用分布式锁来使用分布式锁,这就意味着确保只有一个客户端可以同时操作共享资源。

1.1举个很长的例子,系统a是电子商务系统,现在是机器的引进。 系统上有用户下单的界面,但用户下单前一定要检查库存,确保库存充足后再向用户下单。 由于系统具有一定的合并,因此请将商品库存保存在Redis中,并在用户订购时更新Redis库存。 此时的系统体系结构如下。

但是,如果在某个时刻Redis中的某个商品的库存为1,就会产生问题。

此时,两个请求同时到来,其中一个请求运行到上图中的步骤3,更新数据库的库存为0,但步骤4尚未执行。

另一个请求执行到步骤2,如果发现库存仍为1,则转到步骤3。 结果卖了两个商品,其实只有一个库存。

很明显不是啊。 这就是典型的库存超卖问题此时,您可以轻松地找到一个解决方案,锁定并执行2、3和4个步骤,然后让另一个线程进入并执行步骤2。

如上图所示,执行步骤2时,使用Java提供的同步或ReentrantLock解除锁定,在执行完步骤4后解除锁定。

然后,两个、三个和四个步骤被“锁定”,并在多个线程之间执行串行化执行

由于系统整体的同时上升,一台机器扛不住了。 现在,要添加机器,如下图所示。

增加机器后,系统如上图所示。 这时,如果两个用户的要求同时来,但落在不同的机器上,就会出现这两个要求是可以同时执行,还是库存过剩的问题。

由于上图中的两个a系统运行在两个不同的JVM中,因此他们所添加的锁定仅对属于其JVM中的线程有效,而对其他JVM中的线程无效。

因此,这里的问题是,Java提供的本机阻止机制在多机部署场景中被禁用。 这是因为两台机器没有锁定相同的锁(两个被锁定在不同的JVM上)。

那么,只要保证两台机器上的锁是同一把锁,问题不就解决了吗? 这个时候,分散摇滚隆重登场了。

分布式锁定的想法是在整个系统提供一个全局、唯一的获取锁的“东西”,然后每个系统在需要加锁时,都去问这个“东西”拿到一把锁,这样不同的系统拿到的就可以认为是同一把锁。

关于这个“东西”,可以是Redis、Zookeeper或数据库。 此时的体系结构如下。

上述分析表明,在分布式部署系统中,使用Java本机锁定机制不能保证库存外包场景的线程安全,因此必须使用分布式锁定方案。

2 .在设计高效的分布式锁定分布式锁定时,应该考虑分布式锁定必须满足的至少一些条件,同时考虑如何有效地设计分布式锁定,必须考虑以下几点:

http://www.Sina.com/http://www.Sina.com /

对于分布式高并发,必须确保同一时间只能锁定一个线程。 这是基本点。

http://www.Sina.com/http://www.Sina.com /

在分布式高并发条件下,例如一个线程获得了锁定,但还没有时间解锁,由于系统故障或其他原因无法执行解锁命令,其他线程无法获得锁定,导致(1)

http://www.Sina.com/http://www.Sina.com /

对于具有大量访问权限的共享资源,必须考虑减少锁定等待时间,以避免阻止大量线程。

所以在摇滚的设计中,需要考虑两点。

1、尽量减小片的粒度。 例如,如果使用密钥减少库存,则此密钥的名称可以设置为商品的ID,而不是任何名称。 这个锁只对现在的商品有效,锁的颗粒度小。

2、尽量缩小钥匙范围。 例如,如果锁定两行代码可以解决问题,请不要锁定十行代码。

http://www.Sina.com/http://www.Sina.com /

我们知道ReentrantLock是一个可重载的锁。 其特点是同一线程可以重复获取同一资源的锁定。 再锁定非常有利于资源的高效利用。 关于这一点,我稍后会做演示。

3 .实现基于redi的分布式锁定3.1使用redi命令实现分布式锁定3.1.1锁定实际上位于redi中

s中,给Key键设置一个值,为避免死锁,并给定一个过期时间。

使用的命令**:SET lock_key random_value NX PX 5000**

值得注意的是:

random_value 是客户端生成的唯一的字符串。

NX 代表只在键不存在时,才对键进行设置操作。

PX 5000 设置键的过期时间为5000毫秒。

也可以使用另外一条命令:SETNX key value

只不过过期时间无法设置。

这样,如果上面的命令执行成功,则证明客户端获取到了锁。

3.1.2解锁

解锁的过程就是将Key键删除,但要保证安全性,举个例子:客户端1的请求不能将客户端2的锁给删除掉。

释放锁涉及到两条指令,这两条指令不是原子性的,需要用到redis的lua脚本支持特性,redis执行lua脚本是原子性的。脚本如下:

if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end

这种方式比较简单,但是也有一个最重要的问题:锁不具有可重入性

3.2使用Redisson实现分布式锁 3.2.1Redisson介绍

Redisson是架设在Redis基础上的一个Java驻内存数据网格(In-Memory Data Grid)。充分的利用了Redis键值数据库提供的一系列优势,基于Java实用工具包中常用接口,为使用者提供了一系列具有分布式特性的常用工具类。使得原本作为协调单机多线程并发程序的工具包获得了协调分布式多机多线程并发系统的能力,大大降低了设计和研发大规模分布式系统的难度。同时结合各富特色的分布式服务,更进一步简化了分布式环境中程序相互之间的协作。

3.2.2Redisson简单使用 Config config = new Config(); config.useClusterServers() .addNodeAddress("redis://192.168.31.101:7001") .addNodeAddress("redis://192.168.31.101:7002") .addNodeAddress("redis://192.168.31.101:7003") .addNodeAddress("redis://192.168.31.102:7001") .addNodeAddress("redis://192.168.31.102:7002") .addNodeAddress("redis://192.168.31.102:7003"); RedissonClient redisson = Redisson.create(config); RLock lock = redisson.getLock("anyLock"); lock.lock(); lock.unlock();

只需要通过它的 API 中的 Lock 和 Unlock 即可完成分布式锁,而且考虑了很多细节:

l Redisson 所有指令都通过 Lua 脚本执行,Redis 支持 Lua 脚本原子性执行

l Redisson 设置一个 Key 的默认过期时间为 30s,但是如果获取锁之后,会有一个WatchDog每隔10s将key的超时时间设置为30s。

另外,Redisson 还提供了对 Redlock 算法的支持,它的用法也很简单:

RedissonClient redisson = Redisson.create(config); RLock lock1 = redisson.getFairLock("lock1"); RLock lock2 = redisson.getFairLock("lock2"); RLock lock3 = redisson.getFairLock("lock3"); RedissonRedLock multiLock = new RedissonRedLock(lock1, lock2, lock3); multiLock.lock(); multiLock.unlock(); 3.2.3Redisson原理分析

(1) 加锁机制

线程去获取锁,获取成功: 执行lua脚本,保存数据到redis数据库。

线程去获取锁,获取失败: 一直通过while循环尝试获取锁,获取成功后,执行lua脚本,保存数据到redis数据库。

(2) WatchDog自动延期机制

在一个分布式环境下,假如一个线程获得锁后,突然服务器宕机了,那么这个时候在一定时间后这个锁会自动释放,也可以设置锁的有效时间(不设置默认30秒),这样的目的主要是防止死锁的发生。但是在实际情况中会有一种情况,业务处理的时间可能会大于锁过期的时间,这样就可能**导致解锁和加锁不是同一个线程。**所以WatchDog作用就是Redisson实例关闭前,不断延长锁的有效期。

如果程序调用加锁方法显式地给了有效期,是不会开启后台线程(也就是watch dog)进行延期的,如果没有给有效期或者给的是-1,redisson会默认设置30s有效期并且会开启后台线程(watch dog)进行延期

多久进行一次延期:(默认有效期/3),默认有效期可以设置修改的,即默认情况下每隔10s设置有效期为30s

(3) 可重入加锁机制

Redisson可以实现可重入加锁机制的原因:

l Redis存储锁的数据类型是Hash类型

l Hash数据类型的key值包含了当前线程的信息

下面是redis存储的数据

这里表面数据类型是Hash类型,Hash类型相当于我们java的 <key,<key1,value>> 类型,这里key是指 ‘redisson’

它的有效期还有9秒,我们再来看里们的key1值为078e44a3-5f95-4e24-b6aa-80684655a15a:45它的组成是:

guid + 当前线程的ID。后面的value是就和可重入加锁有关。value代表同一客户端调用lock方法的次数,即可重入计数统计。

举图说明

上面这图的意思就是可重入锁的机制,它最大的优点就是相同线程不需要在等待锁,而是可以直接进行相应操作。

3.2.4 获取锁的流程

其中的指定字段也就是hash结构中的field值(构成是uuid+线程id),即判断锁是否是当前线程

3.2.5 加锁的流程

3.2.6 释放锁的流程

4. 使用Redis做分布式锁的缺点

Redis有三种部署方式

l 单机模式

l Master-Slave+Sentienl选举模式

l Redis Cluster模式

如果采用单机部署模式,会存在单点问题,只要 Redis 故障了。加锁就不行了

采用 Master-Slave 模式,加锁的时候只对一个节点加锁,即便通过 Sentinel 做了高可用,但是如果 Master 节点故障了,发生主从切换,此时就会有可能出现锁丢失的问题。

基于以上的考虑,Redis 的作者也考虑到这个问题,他提出了一个 RedLock 的算法。

这个算法的意思大概是这样的:假设 Redis 的部署模式是 Redis Cluster,总共有 5 个 Master 节点。

通过以下步骤获取一把锁:

获取当前时间戳,单位是毫秒。轮流尝试在每个 Master 节点上创建锁,过期时间设置较短,一般就几十毫秒。尝试在大多数节点上建立一个锁,比如 5 个节点就要求是 3 个节点(n / 2 +1)。客户端计算建立好锁的时间,如果建立锁的时间小于超时时间,就算建立成功了。要是锁建立失败了,那么就依次删除这个锁。只要别人建立了一把分布式锁,你就得不断轮询去尝试获取锁。

但是这样的这种算法,可能会出现节点崩溃重启,多个客户端持有锁等其他问题,无法保证加锁的过程一定正确。例如:

假设一共有5个Redis节点:A, B, C, D, E。设想发生了如下的事件序列:

(1)客户端1成功锁住了A, B, C,获取锁成功(但D和E没有锁住)。

(2)节点C崩溃重启了,但客户端1在C上加的锁没有持久化下来,丢失了。

(3)节点C重启后,客户端2锁住了C, D, E,获取锁成功。

这样,客户端1和客户端2同时获得了锁(针对同一资源)。

最后总结我的面试经验

2021年的金三银四一眨眼就到了,对于很多人来说是跳槽的好机会,大厂面试远没有我们想的那么困难,摆好心态,做好准备,你也可以的。

另外,面试中遇到不会的问题不妨尝试讲讲自己的思路,因为有些问题不是考察我们的编程能力,而是逻辑思维表达能力;最后平时要进行自我分析与评价,做好职业规划,不断摸索,提高自己的编程能力和抽象思维能力。

资料领取方式:点击这里免费获取

BAT面试经验

实战系列:Spring全家桶+Redis等

其他相关的电子书:源码+调优

面试真题:

**

[外链图片转存中…(img-010Rb9iL-1623893838941)]

BAT面试经验

实战系列:Spring全家桶+Redis等

[外链图片转存中…(img-q2s0nvwV-1623893838942)]

其他相关的电子书:源码+调优

[外链图片转存中…(img-TaR2yxgE-1623893838943)]

面试真题:

[外链图片转存中…(img-rF31TFiE-1623893838944)]

[外链图片转存中…(img-G0B9crwi-1623893838945)]

javaweb酒店管理系统,一般酒店前台存在的问题