科大讯飞一面
sycronized和locked的区别?
Synchronized编码更简单,锁机制由JVM维护,在竞争不激烈的情况下性能更好。Lock功能更强大更灵活,竞争激烈时性能较好。
区别如下:
- 来源:lock是一个接口,而synchronized是java的一个关键字,synchronized是内置的语言实现;
- 异常是否释放锁:synchronized在发生异常时候会自动释放占有的锁,因此不会出现死锁;而lock发生异常时候,不会主动释放占有的锁,必须手动unlock来释放锁,可能引起死锁的发生。(所以最好将同步代码块用try catch包起来,finally中写入unlock,避免死锁的发生。)
- 是否响应中断lock等待锁过程中可以用interrupt来中断等待,而synchronized只能等待锁的释放,不能响应中断;
- 是否知道获取锁:Lock可以通过trylock来知道有没有获取锁,而synchronized不能;
- Lock可以提高多个线程进行读操作的效率。(可以通过readwritelock实现读写分离)
- 在性能上来说,如果竞争资源不激烈,两者的性能是差不多的,而当竞争资源非常激烈时(即有大量线程同时竞争),此时Lock的性能要远远优于synchronized。
hashmap put的流程
图片
HashMap HashMap的put()方法用于向HashMap中添加键值对,当调用HashMap的put()方法时,会按照以下详细流程执行(JDK8 1.8版本):
第一步:根据要添加的键的哈希码计算在数组中的位置(索引)。
第二步:检查该位置是否为空(即没有键值对存在)
- 如果为空,则直接在该位置创建一个新的Entry对象来存储键值对。将要添加的键值对作为该Entry的键和值,并保存在数组的对应位置。将HashMap的修改次数(modCount)加1,以便在进行迭代时发现并发修改。
第三步:如果该位置已经存在其他键值对,检查该位置的第一个键值对的哈希码和键是否与要添加的键值对相同?
- 如果相同,则表示找到了相同的键,直接将新的值替换旧的值,完成更新操作。
第四步:如果第一个键值对的哈希码和键不相同,则需要遍历链表或红黑树来查找是否有相同的键:
如果键值对集合是链表结构,从链表的头部开始逐个比较键的哈希码和equals()方法,直到找到相同的键或达到链表末尾。
- 如果找到了相同的键,则使用新的值取代旧的值,即更新键对应的值。
- 如果没有找到相同的键,则将新的键值对添加到链表的头部。
如果键值对集合是红黑树结构,在红黑树中使用哈希码和equals()方法进行查找。根据键的哈希码,定位到红黑树中的某个节点,然后逐个比较键,直到找到相同的键或达到红黑树末尾。
- 如果找到了相同的键,则使用新的值取代旧的值,即更新键对应的值。
- 如果没有找到相同的键,则将新的键值对添加到红黑树中。
第五步:检查链表长度是否达到阈值(默认为8):
- 如果链表长度超过阈值,且HashMap的数组长度大于等于64,则会将链表转换为红黑树,以提高查询效率。
第六步:检查负载因子是否超过阈值(默认为0.75):
- 如果键值对的数量(size)与数组的长度的比值大于阈值,则需要进行扩容操作。
第七步:扩容操作:
- 创建一个新的两倍大小的数组。
- 将旧数组中的键值对重新计算哈希码并分配到新数组中的位置。
- 更新HashMap的数组引用和阈值参数。
第八步:完成添加操作。
此外,HashMap是非线程安全的,如果在多线程环境下使用,需要采取额外的同步措施或使用线程安全的ConcurrentHashMap。
avl树和红黑树的区别?
- 平衡二叉树追求的是一种 “完全平衡” 状态:任何结点的左右子树的高度差不会超过 1,优势是树的结点是很平均分配的。这个要求实在是太严了,导致每次进行插入/删除节点的时候,几乎都会破坏平衡树的第二个规则,进而我们都需要通过左旋和右旋来进行调整,使之再次成为一颗符合要求的平衡树。
- 红黑树不追求这种完全平衡状态,而是追求一种 “弱平衡” 状态:整个树最长路径不会超过最短路径的 2 倍。优势是虽然牺牲了一部分查找的性能效率,但是能够换取一部分维持树平衡状态的成本。与平衡树不同的是,红黑树在插入、删除等操作,不会像平衡树那样,频繁着破坏红黑树的规则,所以不需要频繁着调整,这也是我们为什么大多数情况下使用红黑树的原因。
红黑树插入的时间复杂度是多少?
红黑树平衡,插入、删除、查找操作的时间复杂度都是O(logn)。
如何给数据库中数据加行级锁?
InnoDB 引擎是支持行级锁的,而 MyISAM 引擎并不支持行级锁。
普通的 select 语句是不会对记录加锁的,因为它属于快照读。如果要在查询时对记录加行锁,可以使用下面这两个方式,这种查询会加锁的语句称为锁定读。
//对读取的记录加共享锁
select ... lock in share mode;
//对读取的记录加独占锁
select ... for update;
上面这两条语句必须在一个事务中,因为当事务提交了,锁就会被释放,所以在使用这两条语句的时候,要加上 begin、start transaction 或者 set autocommit = 0。
共享锁(S锁)满足读读共享,读写互斥。独占锁(X锁)满足写写互斥、读写互斥。
图片
行级锁的类型主要有三类:
- Record Lock,记录锁,也就是仅仅把一条记录锁上;
- Gap Lock,间隙锁,锁定一个范围,但是不包含记录本身;
- Next-Key Lock:Record Lock + Gap Lock 的组合,锁定一个范围,并且锁定记录本身。
Record Lock
Record Lock 称为记录锁,锁住的是一条记录。而且记录锁是有 S 锁和 X 锁之分的:
- 当一个事务对一条记录加了 S 型记录锁后,其他事务也可以继续对该记录加 S 型记录锁(S 型与 S 锁兼容),但是不可以对该记录加 X 型记录锁(S 型与 X 锁不兼容);
- 当一个事务对一条记录加了 X 型记录锁后,其他事务既不可以对该记录加 S 型记录锁(S 型与 X 锁不兼容),也不可以对该记录加 X 型记录锁(X 型与 X 锁不兼容)。
举个例子,当一个事务执行了下面这条语句:
mysql > begin;
mysql > select * from t_test where id = 1 for update;
就是对 t_test 表中主键 id 为 1 的这条记录加上 X 型的记录锁,这样其他事务就无法对这条记录进行修改了。
图片
当事务执行 commit 后,事务过程中生成的锁都会被释放。
Gap Lock
Gap Lock 称为间隙锁,只存在于可重复读隔离级别,目的是为了解决可重复读隔离级别下幻读的现象。
假设,表中有一个范围 id 为(3,5)间隙锁,那么其他事务就无法插入 id = 4 这条记录了,这样就有效的防止幻读现象的发生。
图片
间隙锁虽然存在 X 型间隙锁和 S 型间隙锁,但是并没有什么区别,间隙锁之间是兼容的,即两个事务可以同时持有包含共同间隙范围的间隙锁,并不存在互斥关系,因为间隙锁的目的是防止插入幻影记录而提出的。
Next-Key Lock
Next-Key Lock 称为临键锁,是 Record Lock + Gap Lock 的组合,锁定一个范围,并且锁定记录本身。
假设,表中有一个范围 id 为(3,5] 的 next-key lock,那么其他事务即不能插入 id = 4 记录,也不能修改 id = 5 这条记录。
图片
所以,next-key lock 即能保护该记录,又能阻止其他事务将新纪录插入到被保护记录前面的间隙中。
next-key lock 是包含间隙锁+记录锁的,如果一个事务获取了 X 型的 next-key lock,那么另外一个事务在获取相同范围的 X 型的 next-key lock 时,是会被阻塞的。
比如,一个事务持有了范围为 (1, 10] 的 X 型的 next-key lock,那么另外一个事务在获取相同范围的 X 型的 next-key lock 时,就会被阻塞。
虽然相同范围的间隙锁是多个事务相互兼容的,但对于记录锁,我们是要考虑 X 型与 S 型关系,X 型的记录锁与 X 型的记录锁是冲突的。
如果和redis无法连接,如何排查原因?
- 网络闪断:先排查网络问题,比如检查网络连接是否正常。确保网络连接稳定,没有断开或中断。检查网络带宽是否耗尽。如果网络带宽达到极限,可能会导致闪断。你可以联系网络管理员或提供商以解决带宽问题。
- Redis连接拒绝:确认maxclients设置。在Redis配置文件(redis.conf)中,找到maxclients设置项,确保其值足够大以容纳你的并发连接数。你可以通过修改配置文件来增加maxclients的值。
- 连接溢出:进程可打开的最大文件数控制(ulimit -n)是限制系统中同时存在的文件描述符数量的设置。对于Redis来说,高并发情况下需要处理大量的连接,因此需要增大该值。
如果redis内存不足,你认为是什么原因
- 没有开启内存淘汰策略,导致 Redis 无法运用内存淘汰策略来淘汰不常用的内存数据。
- 在 Redis 长时间运行的情况下,由于频繁的插入和删除操作,可能会导致内存碎片,导致可用内存减少。
- 单 Redis 节点的内存无法扛住激增的用户数据,这时候需要考虑构建 Redis 集群来应对内存不足的问题。
为什么 redis购物车用hash不用 string
使用 Hash 类型来实现购物车有几个明显的优势,相比使用 String 类型:
- 结构化数据:Hash 类型允许将购物车中的每个商品表示为一个字段-值对(field-value pair),其中字段可以是商品的 ID,值可以是商品的数量或其他属性。这种结构化可以使得数据更加清晰和易于管理
- 节省内存:在 Redis 中,Hash 类型对于存储小数量的字段-值对(例如购物车中的商品)通常会使用更高效的内存编码方式。当 Hash 中的成员少于一定数量时,Redis 会使用压缩算法来减少内存消耗。
- 操作灵活性:使用 Hash 类型,你可以对购物车进行更复杂的操作,比如只更新某个商品的数量,而不需要将整个购物车类型的数据取出和再存储。对于多个购物车的操作,Hash 可以显著提高效率。
- 方便查询与修改:使用 Hash 类型的购物车,可以更方便地查询和修改特定商品的信息,例如获取某个商品的数量,非常简单,只需要进行一次 HGET 操作。
讲一下Nginx的负载均衡策略
Nginx支持的负载均衡算法包括:
- 轮询:按照顺序依次将请求分配给后端服务器。这种算法最简单,但是也无法处理某个节点变慢或者客户端操作有连续性的情况。
- IP哈希:根据客户端IP地址的哈希值来确定分配请求的后端服务器。适用于需要保持同一客户端的请求始终发送到同一台后端服务器的场景,如会话保持。
- URL哈希:按访问的URL的哈希结果来分配请求,使每个URL定向到一台后端服务器,可以进一步提高后端缓存服务器的效率。
- 最短响应时间:按照后端服务器的响应时间来分配请求,响应时间短的优先分配。适用于后端服务器性能不均的场景,能够将请求发送到响应时间快的服务器,实现负载均衡。
- 加权轮询:按照权重分配请求给后端服务器,权重越高的服务器获得更多的请求。适用于后端服务器性能不同的场景,可以根据服务器权重分配请求,提高高性能服务器的利用率。
linux 命令怎么看 cpu 占用率?
可以通过 top 命令来查看系统的 cpu 占用率和各个进程的 cpu 占用率。
图片
死锁的时候cpu利用率是高还是低?为什么?
这个关键是看用了什么锁:
- 如果是用了自旋锁,拿不到锁的时候,忙等待,反复探测锁状态,直到拿到锁,进入临界区,这种情况会消耗CPU,如果发生死锁的话,cpu 利用率就会比较高。
- 如果是互斥锁,拿不到锁就让线程休眠的,这时候就相当于放弃了 cpu,不会消耗 cpu,如果发生死锁的话,cpu 利用率就不会升高的。
linux 命令怎么看进程占用的端口?
可以通过 lsof 或者 netstate 命令查看,比如查看 80 端口。
lsof :
[root@xiaolin ~]# lsof -i :80
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
nginx 929 root 6u IPv4 15249 0t0 TCP *:http (LISTEN)
nginx 929 root 7u IPv6 15250 0t0 TCP *:http (LISTEN)
nginx 934 nginx 6u IPv4 15249 0t0 TCP *:http (LISTEN)
nginx 934 nginx 7u IPv6 15250 0t0 TCP *:http (LISTEN)
AliYunDun 16507 root 10u IPv4 40212783 0t0 TCP xiaolin:41830->100.100.30.26:http (ESTABLISHED)
netstate:
[root@xiaolin ~]# netstat -napt | grep 80
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 929/nginx: master p