1. volatile 是轻量级锁:
只能修饰在变量上,使得 cpu 每次对于该变量的修改和读取都从内存中操作,而不是从CPU cache 中操作,保证共享变量对所有线程的可见性,但是并不能保证原子性
2. synchronized 悲观锁:
当某个线程访问被 synchronized 修饰的方法或代码块时,会先检查有没有其他线程在占用该对象锁,如果有,则该线程进入阻塞状态,直到占用该锁的线程使用完毕释放锁;synchronized 能够保证同一时刻只能有一个线程能访问,可修饰在类,方法,变量上,保证多线程下操作的可见性和原子性
缺点:由于线程阻塞引起的线程频繁睡眠和唤醒极大的消耗了 CPU 资源
3. CAS 自旋锁:
当多个线程竞争一个共享资源时,只有一个线程会胜出,但是其它竞争失败的线程并不会放弃占用cpu,而是循环刷新访问对于该资源锁的请求,直到获取该资源的锁;CAS只能保证变量的原子性并不能保证变量的可见性,所以一般都是使用 CAS + Volatile 实现变量的多线程
1)JUC 并发包中的原子类都存放在 java.util.concurrent.atomic 类路径下
2)CAS 原理:
CAS 内部维护需要读写的内存值(V)、expecValue(A)、newValue(B)
3)简单测试
可以看到expectValue = 11,newValue = 20,因为内存旧值 = 10,不等于 expectValue,说明在这段时间内有其他线程操作了对象 atomicInteger ,修改了其值,所以本次修改失败,修改结果res = false,atomicInteger.get() = 10
import java.util.concurrent.atomic.AtomicInteger;
public class TestConCurrent {
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(10);
boolean res = atomicInteger.compareAndSet(11, 20);
System.out.println("原子对象的修改结果:"+res);
System.out.println(atomicInteger.get());
}
}
4)自旋CAS缺点:
- 未获得锁的线程如果一直循环执行,会给CPU带来非常大的执行开销
- CAS 保证的是对一个变量执行操作的原子性,如果对多个变量操作时,CAS 目前无法直接保证操作的原子性
- ABA 问题:当某线程提交操作进行比较时发现内存数据旧数据A和预期值A一致,但是可能该数据被其他线程修改成B,然后又被其他线程修改回A;真正要做到严谨的CAS机制,我们在compare阶段不仅要比较期望值A和地址V中的实际值,还要比较变量的版本号是否一致