> 文章列表 > 《死锁》与《CAS ABA》问题

《死锁》与《CAS ABA》问题

《死锁》与《CAS ABA》问题

文章目录

    • 什么是死锁
    • 常见死锁情况❗️
    • 死锁的必要条件❗️
    • 如何避免死锁呢?
    • CAS
    • CAS中ABA问题
    • 解决ABA问题

什么是死锁

死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象 。

常见死锁情况❗️

1.一个线程一把锁

//伪代码
synchronized(lock1){synchronized(lock1){...}...
}

我们以第一次加锁可以成功,当我们进行第二次加锁的时候就出现麻烦了,可以发现我们当前的锁对象已经被占用了,我们就会不断的去尝试加锁,此时就导致了死循环。

2、两个线程两把锁

//伪代码线程 t1
synchronized(lock1){synchronized(lock2){...}
}//伪代码线程 t2
synchronized(lock2){synchronized(lock1){...}
}

假设有两个线程,线程t1和线程t2,当线程t1和t2同时开始执行,t1获取到了lock1锁对象,t2获取到lock2的锁对象,当t1去获取lock2时发现已经被占用,t2去获取lock1时发现也被占用,此时就发生了死锁。

3、N个线程M把锁

线程数量和锁的数量更多了,更容易死锁。

死锁的必要条件❗️

死锁的发生必须具备以下四个必要条件:
1.互斥使用:一个锁被一个线程占用后,其他线程使用不了(锁本质,保证原子性)。
2.不可抢占:一个锁被一个线程占用后,其他线程不能将锁抢占。
3.请求和保持:当一个线程占据多把锁后,除非显式释放锁,否则锁一直被该线程锁占用。
4.环路等待:多个线程等待关系闭环了,比如A等B,B等C,C等A

如何避免死锁呢?

死锁是一个比较严重的bug,实践中如何避免死锁呢?
一个简单的条件,破解循环等待~
针对锁进行编号,如果需要获取多把锁,约定加锁顺序,务必是先对小的编号加锁,后对编号大的枷锁。

//伪代码线程 t1
synchronized(lock1){synchronized(lock2){...}
}//伪代码线程 t2
synchronized(lock1){synchronized(lock2){...}
}

CAS

CAS概念:CAS通过比较内存中的一个数据是否是预期值,如果是就将它修改成新值,如果不是则进行自旋(从新获取新值),重复比较的操作,直到某一刻内存值等于预期值再进行修改。

《死锁》与《CAS ABA》问题
寄存器A的值如果比较内存M的值相等,那么就把寄存器B的值赋给M。CAS操作,是一条CPU指令,并非是一段代码!这一条指令能完成上述一段代码的的功能。

CAS中ABA问题

首先线程1进行读取共享变量A的值,由于种种原因线程挂起,在挂起时线程2执行把A的值改了,又改回来了,此后线程1挂起恢复,判断值一样(寄存器A和内存M),进行一系列操作,此时是会有bug的。
有些业务可能不需要关心中间过程,只要前后值一样就行,但是有些业务需求要求变量在中间过程不能被修改。

解决ABA问题

我们要想解决ABA问题我们需要引入版本号(当有修改操作版本号+1),此时再判断版本号相不相同,不相同则放弃此次CAS操作。