什么是悲观锁和乐观锁?都有哪些具体的实现?
悲观锁:
他默认每次对共享资源访问都可能引发冲突(例如数据被意外修改).
Java 中synchronized和ReentrantLock等独占锁就是悲观锁思想的实现。【互斥】
在高并发环境下,如果多个线程频繁争抢锁资源,会导致大量线程被阻塞。这些被阻塞的线程会引发频繁的线程上下文切换,从而增加系统的额外性能消耗。此外,悲观锁还可能引发死锁问题,使程序无法正常执行,进一步影响系统稳定性。
乐观锁:
它是一种乐观的并发策略,它默认多个线程在访问共享资源的时候不会发生冲突,不会造成堵塞线程执行,也无需等待.
核心思想是:线程在修改数据的时候,先检查该数据是否被其他线程修改过,如果没有则提交更新,否则重试或者放弃操作.
CAS:
CAS操作包含三个操作数(CAS算法实现):
1.变量的真实内存值(V)
2.预期值(A)
3.新值(B)
工作原理:
1.比较:首先比较内存值V是否与预期值A相等
2.交换:如果相等,处理器会自动将该位置值更新为新值B
CAS的伪代码表示:
CAS是一种无锁的并发并发控制技术,通过硬件级原子操作来保证多线程环境下数据更新安全,
在更新某个变量的值之前,先判断该变量当前的值是否与我之前读取的值一致(无其他线程修改),如果一致则更新为新值;如果不一致则放弃更新,返回失败
什么是 ABA 问题?如何解决?
ABA问题就是在变量V初始读取A,后续CAS操作时仍为A,但这不能在保证期间违背修改过,因为可能存在这样的时序:V被改为B后又改回A,导致CAS误判变量未被改动。这种中间状态被隐藏的情况就是典型的ABA问题.
解决:解决ABA问题的常见方案是为变量添加版本号。Java在1.5版本后提供的AtomicStampedReference类专门处理这类问题,其核心机制是:在执行compareAndSet操作时,不仅会比较变量的当前值,还会验证附加的版本标记,只有两者都符合预期才会执行更新操作。这种设计通过引入状态标记来识别中间变化,有效避免了传统CAS操作可能出现的误判情况。
JMM-Java内存模型:
JMM 即 Java Memory Model,它定义了主存、本地内存的抽象概念,底层对应着 CPU 寄存器、缓存、硬件内存、 CPU 指令优化等。
存在的问题:
CPU的处理速度极快,而内存访问相对较慢(大约相差100倍左右)。
导致了一个现象:
当CPU需要读取主存中的变量时,它不得不停下来等待
性能瓶颈:CPU宝贵的计算能力被浪费在等待内存数据上
JDK 1.2 之后,线程可以把主存中的变量保存到自己的本地内存(每个线程私有的内存),而不是直接在主存中进行读写,就不需要将时间浪费在等待内存数据上,每次只需要从本地内存中读取到对应的变量值即可。
这种本地内存和主内存模型的设计,就是 JMM.
什么是主内存?什么是本地内存?
1.主存:所有线程创建的实例对象都存放在主内存中
2.本地内存:每个线程都有一个私有的本地内存,本地内存存储了该线程可以操作的共享变量的副本。每个线程只能操作自己本地内存中的变量,无法直接访问其他线程的本地内存。本地内存是 JMM 抽象出来的一个概念,并不真实存在,它涵盖了CPU高速缓存、寄存器以及其他的硬件和编译器优化。
JDK1.2 之前和之后的内存模型有什么区别?分别有什么缺点?
在JDK1.2之前,java的内存模型都是从主存中读取变量CPU需要读取主存中的变量时,它不得不停下来等待,性能瓶颈:CPU宝贵的计算能力被浪费在等待内存数据上.
在JDK1.2之后,线程可以把主存中的变量保存到自己本地内存(每个线程私有的内存),而不是直接的在主存中续写,就不需要将时间浪费在等待内存数据上,每次只需要从本地内存中读取到对应的变量即可,这种本地内存和主内存模型的设计就是JMM.
什么是可见性问题,如何解决可见性问题:
可见性问题.如果一个线程对一个共性变量进行了修改,而其他线程不能及时读到修改后的值.
解决方法:给对应的变量加上volatile(可以用来修饰成员变量和静态成员变量),线程只要检测到volatile修饰变量的值被修改,就会重新从主内存进行加载。
什么是指令重排?多线程情况下指令重排有什么问题?
JVM 会在不影响结果正确性的前提下,可以调整语句的执行顺序,
static int i;
static int j;
// 在某个线程内执行如下赋值操作
i = 1;
j = 2;
可以看到,至于是先执行 i 还是 先执行 j ,对最终的结果不会产生影响。
所以,上面代码真正执行时,既可以是
i = 1;
j = 2;
也可以是
j = 2;
i = 1;
这种特性称之为指令重排,多线程下的指令重排会产生线程安全问题
I_Result 是一个对象,有一个属性 r1 用来保存结果,问可能的结果有几种?
正常情况
◦
情况1:线程1 先执行,这时 ready = false,所以进入 else 分支结果为 1
◦
情况2:线程2 先执行 num = 2,但没来得及执行 ready = true,线程1 执行,还是进入 else 分支,结果为 1
◦
情况3:线程2 执行到 ready = true,线程1 执行,这回进入 if 分支,结果为 4(因为 num = 2 已经执行过了)
意外情况(actor2方法中,ready = true被指令重排到 num = 2前面啦 ):
◦
情况4:线程2 执行 ready = true,切换到线程1,进入 if 分支,相加为 0,再切回线程2 执行 num = 2,结果为 0
什么是有序性问题?怎么能解决有序性问题?
有序性问题:由于指令重排导致真实结果和我们的预期结果不一致
解决方法:使用 volatile 修饰的变量,可以禁用指令重排
Volatile如今保证可见性和有序性问题?能保证原子性吗
- 保证可见性:
volatile变量的写操作会立即刷新到主内存,读操作会从主内存中读取最新值,这样就保证了不同线程对该变量的可见性。 - 保证有序性:
volatile关键字通过内存屏障禁止了指令重排,从而保证了变量操作的有序性。 - 原子性:
volatile不能保证原子性。例如,对volatile修饰的变量进行自增(i++)操作,这是一个复合操作(读取 - 修改 - 写入),在多线程环境下,可能会出现线程安全问题,无法保证操作的原子性。 - volatile不能解决指令交错问题,所以不能保证原子性
volatile 和 synchronized 有什么区别?

42万+

被折叠的 条评论
为什么被折叠?



