原子性
什么是原子性,比如事务的一大特征就是原子性,即要么全部执行、要么都不执行。
在JAVA中,如线程A、线程B 同时执行一个代码块,我们常用Synchroized或者其他形式锁来保证同一时刻只有一个线程执行。这就是我们常用的锁机制,锁机制是一种颗粒度大、比较粗糙的机制。对于计数器这样的我们在用锁 岂不是 牛刀杀小鸡了,这里JAVA就提供了Atomic原子操作类。 原子操作类是基于CAS机制的。
什么是CAS?
CAS(Compare And Swap,比较并交换)是一种重要的并发控制机制,广泛应用于多线程编程中实现无锁(lock-free)算法。
CAS是一种原子操作,包含三个操作数:
-
主内存中存放的共享变量的值:V(一般情况下这个V是内存的地址值,通过这个地址可以获得内存中的值)。
-
工作内存中共享变量的副本值,也叫预期值:A。
-
需要将共享变量更新到的最新值:B。
操作逻辑:
1、读取旧值:线程读取内存位置 V 的当前值 A。
2、比较旧值:将读取的值 A 与预期的旧值进行比较。
3、交换值:如果当前值与预期值相同,则用新值 B 替换当前值。这个替换操作是原子的,即要么完全完成,要么完全不发生。
4、返回结果:如果替换成功,则返回 true 或成功信号;否则返回 false 或失败信号,表示旧值已被其他线程更改。
CAS操作通过一条CPU的原子指令(如cmpxchg指令)实现,保证了比较和更新的原子性。在执行CAS操作时,CPU会判断当前系统是否为多核系统,如果是,则会给总线加锁,确保只有一个线程能够执行CAS操作。这种独占式的原子性实现方式,比起使用synchronized等重量级锁,具有更短的排他时间,因此在多线程情况下性能更佳。
CAS问题
ABA问题,通过增加版本号控制
自旋耗时,设置自旋次数控制
只能保证一个共享变量,把多个共享变量封装成一个共享变量
Atomic原子操作
JAVA提供了一套atomic原子操作类
并发安全相关问题
1.什么是线程安全
线程安全是多线程编程中的核心概念,指的是当多个线程同时访问某个类、对象或方法时,这个类、对象或方法仍然能够保持正确的行为,不需要额外的同步措施。
2.如何保证线程安全
1.线程封闭
我们把对象封装在当前线程内部,只有当前线程能看到此对象。
2.栈封闭
就是使用局部变量,局部变量会被拷贝到当前线程栈中,不共享。
3.ThreadLocal
使用ThreadLocal对线程进行封闭,记得合理remove(),ThreadLocal存在内存泄漏问题。
4.使用无状态类
没有成员变量的类叫做无状态类
5.类不可变
所有成员方法都加上final关键字
6.加锁或CAS
死锁
学术概念:
死锁是多线程编程中最棘手的问题之一,指两个或多个线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力干涉,这些线程都无法继续执行下去。
死锁产生的四个必要条件(Coffman条件)
-
互斥条件:资源一次只能由一个线程占用
-
占有且等待:线程持有至少一个资源,并等待获取其他被占用的资源
-
不可抢占条件:已分配给线程的资源,不能被其他线程强行夺取
-
循环等待条件:多个线程形成头尾相接的循环等待资源关系
理解概念:
多个操作者(M>=2)争夺多个资源(N>=2)
操作顺序不一致
拿到资源不放手
排查死锁
使用jstack分析