面试专题-7、八股文
1. 死锁
❕ ^mpr2re
死锁,简单来说就是两个或者两个以上的线程在执行的过程中,争夺同一个共享资源造成的相互等待的现象。 如果没有外部干预,线程会一直阻塞无法往下执行,这些一直处于相互等待资源的线程就称为死锁线程。
导致死锁的条件有四个,这四个条件同时满足就会产生死锁:
- 互斥条件,共享资源 X 和 Y 只能被一个线程占用;
- 请求和保持条件,线程 T1 已经取得共享资源 X,在等待共享资源 Y 的时候,不释放共享资源 X;
- 不可抢占条件,其他线程不能强行抢占线程 T1 占有的资源;
- 循环等待条件,线程 T1 等待线程 T2 占有的资源,线程 T2 等待线程 T1 占有的资源,就是循环等待。
导致死锁之后,只能通过人工干预来解决,比如重启服务,或者杀掉某个线程。所以,只能在写代码的时候,去规避可能出现的死锁问题。
按照死锁发生的四个条件,只需要破坏其中的任何一个,就可以解决,但是,互斥条件是没办法破坏的,因为这是互斥锁的基本约束,其他三方条件都有办法来破坏:
对于“请求和保持”这个条件,我们可以一次性申请所有的资源,这样就不存在等待了。
对于“不可抢占”这个条件,加入超时机制,占用部分资源的线程进一步申请其他资源时,如果申请不到,可以主动释放它占有的资源,这样不可抢占这个条件就破坏掉了。
并发 Lock 实现,如 ReentrantLock 还支持非阻塞式的获取锁操作 tryLock(),这是一个插队行为(barging),并不在乎等待的公平性,如果执行时对象恰好没有被独占,则直接获取锁。有时,我们希望条件允许就尝试插队,不然就按照现有公平性规则等待,一般采用下面的方法:
1 |
|
- 对于“循环等待”这个条件,可以靠按序申请资源来预防。所谓按序申请,是指资源是有线性顺序的,申请的时候可以先申请资源序号小的,再申请资源序号大的,这样线性化后自然就不存在循环了。
- 尽量避免使用多个锁
1.1. 资料
1 |
|
1 |
|
2. RabbitMQ 如何实现高可用
2.1. 普通集群
这种集群模式下,各个节点只同步元数据,不同步队列中的消息。 其中元数据包含队列的名称、交换机名称及属性、交换机与队列的绑定关系等。 当我们发送消息和消费消息的时候,不管请求发送到 RabbitMQ 集群的哪个节点。 最终都会通过元数据定位到队列所在的节点去存储以及拉取数据。 很显然,这种集群方式并不能保证 Queue 的高可用,因为一旦 Queue 所在的节点挂了,那么这个 Queue 的消息就没办法访问了。 它的好处是通过多个节点分担了流量的压力,提升了消息的吞吐能力。
2.2. 镜像集群
它和普通集群的区别在于,镜像集群中 Queue 的数据会在 RabbitMQ 集群的每 个节点存储一份。一旦任意一个节点发生故障,其他节点仍然可以继续提供服务。 所以这种集群模式实现了真正意义上的高可用。
最后,在镜像集群的模式下,我们可以通过 Keepalived+HAProxy 来实现 RabbitMQ 集群的负载均衡。 其中: HAProxy 是一个能支持四层和七层的负载均衡器,可以实现对 RabbitMQ 集群 的负载均衡同时为了避免 HAProxy 的单点故障,可以再增加 Keepalived 实现 HAProxy 的主备,如果 HAProxy 主节点出现故障那么备份节点就会接管主节点 提供服务。 Keepalived 提供了一个虚拟 IP,业务只需要连接到虚拟 IP 即可。
3. 参考与感谢
❕ ^9xqirb
1 |
|
3.1. 跟着 MIC 学架构
3.1.1. 视频
3.1.2. 资料
[[Java程序员求职突击手册.doc]]
[[MIC老师最新面试文档.pdf]]
3.2. Tom 谈架构
3.2.1. 视频
3.2.2. 资料
[[Tom老师面试资料文档.pdf]]
3.3. 其他文档资料
1 |
|