多线程进阶--面试常见知识点

1 常见的锁策略

1.1 乐观锁vs悲观锁

悲观乐观是对后续锁冲突是否频繁做出的预测。

如果预测接下来发生锁冲突的概率不大,就可以少做一些工作,就称为乐观锁;

如果预测接下来发生锁冲突的概率较大,就需要多做一些工作,就称为悲观锁。

1.2 重量级锁vs轻量级锁

重量级锁,锁的开销较大;轻量级锁,锁的开销较小。

通常,乐观锁就是轻量级锁,悲观锁就是重量级锁。

1.3 自旋锁vs挂起等待锁

自旋锁是轻量级锁的的一种典型实现,往往在用户态进行,例如使用一个while循环,不断判断当前锁是否被释放,如果没释放,就继续循环,如果已经释放,就获取到锁并结束循环。(忙等,消耗cpu但是换来了更快的响应速度);

挂起等待锁要借助系统api来实现,一旦出现锁竞争,就会在系统内核中执行一系列的操作(比如让这个线程进入阻塞状态,不参与cpu的调度,而阻塞的开销通常是很大的)。

1.4 读锁vs写锁

读锁:读的时候别的线程可以读,但是不可写。

写锁:写的时候别的线程不可以读,也不可以写。

1.5 公平锁vs非公平锁

当一堆线程等待锁释放想要拿到锁的时候,该按照什么策略拿呢?

公平锁:先来的线程先拿到锁。(先来后到)

非公平锁:每个线程以相同的概率竞争锁。

以上策略都是锁的一些特点,那前面说过的synchronized属于哪种锁呢?

1、对于"悲观乐观"是自适应的。

2、对于"重量轻量"是自适应的。

3、对于"自旋挂起"是自适应的。

4、不是读写锁。

5、是可冲入锁。

6、是非公平锁。

对于初始情况,synchronized会预测锁冲突的概率不大,此时以乐观锁的模式来运行(也就是轻量并且自旋的);如果后续锁冲突的概率加大,那么此时就会自适应的变为悲观锁(也就是重量挂起的)。

2 CAS

CAS就是compare和swap,也就是比较并且交换,那它比较的是什么呢?是内存和寄存器中的值。

CAS(M,A,B):比较M和A的值,如果相同,就把M和B的值交换,返回true;如果不同,就什么都不做,直接返回false。

这是CAS的伪代码:

CAS本质上是cpu提供的一个指令,是具有原子性的,之后又被操作系统封装,提供api,又被JVM封装,也提供api,最后供程序员使用。

既然CAS是具有原子性的,那么它也可以解决"线程安全"问题,从而在一些场景中代替加锁策略,基于CAS实现"线程安全"的编程称为无锁编程。这样做的好处是什么呢:不仅可以保证线程安全,同时比加锁的效率更高;但是,也存在一些缺点:1、代码会更复杂,不好理解 2、仅使用于一些场景,不如加锁普适性强。

2.1 CAS的关键问题:ABA问题

什么是ABA呢?当一个线程将数据A取出,经过一系列操作后把它变为B,但是最后又通过一系列操作将它变回A,那么对于另一个执行CAS操作的线程来说,可能会认为这个数据是没发生过变化的(虽然有的时候也不会出现bug),这种问题就是ABA问题。

此时,左侧的CAS中value的值不等于oldValue的值,就不执行任何操作,直接返回false。但是如果发生ABA问题,例如此时恰好有个人又给我转了500元,那么我的value又变回1000,等于oldValue,再次扣款,这就出现了很严重的bug,本来我只打算取500,但是现在取了1000!!

那又该怎么解决这个问题呢?

很简单,只需要让判定的数值按照一个方向变化即可(不要出现反复横跳,一会加一会减就可能会出现ABA问题)。在上述这个例子中,我们只需要将CAS的判定条件改为版本号,初识版本号为v1,每执行一次操作,版本号就会加1,那么,只要有其它线程穿插执行,版本号一定不等于old版本号,此时一定不会执行交换操作,而是会直接返回false,bug顺利解决。

3 信号量Semaphore

信号量是操作系统中比较重要的概念,其实信号量就是一个计数器,描述了"可用资源"的个数,每次申请一个可用资源,就要让信号量减1(P操作),每次释放一个操作,就要让信号量加1(V操作),那如果信号量已经为0的情况下继续申请资源(使用P操作)会发生什么呢?此时就会阻塞等待,直到其它线程释放一个资源(使用V操作),才能继续往下执行。

前面提到过的锁其实就是一种特殊的信号量,锁就是可用资源为1的信号量,所以一旦加锁,其它线程就会发生阻塞,因为加锁相当于使信号量减1,而此时信号量一共就是1,所以直到该线程释放锁,也就相当于执行V操作,其它线程才能获得锁

相关推荐
一 乐几秒前
校务管理|基于springboot + vueOA校务管理系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端·spring
LYFlied6 分钟前
【每日算法】LeetCode124. 二叉树中的最大路径和
数据结构·算法·leetcode·面试·职场和发展
摇滚侠15 分钟前
面试实战 问题三十四 对称加密 和 非对称加密 spring 拦截器 spring 过滤器
java·spring·面试
xqqxqxxq16 分钟前
Java 集合框架之线性表(List)实现技术笔记
java·笔记·python
L0CK25 分钟前
RESTful风格解析
java
程序员小假34 分钟前
我们来说说 ThreadLocal 的原理,使用场景及内存泄漏问题
java·后端
何中应36 分钟前
LinkedHashMap使用
java·后端·缓存
tryxr44 分钟前
Java 多线程标志位的使用
java·开发语言·volatile·内存可见性·标志位
talenteddriver1 小时前
java: Java8以后hashmap扩容后根据高位确定元素新位置
java·算法·哈希算法
云泽8081 小时前
STL容器性能探秘:stack、queue、deque的实现与CPU缓存命中率优化
java·c++·缓存