并发编程里死锁、CAS的原理

294 阅读3分钟

一、死锁

概念:

是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁

死锁的发生必须具备以下四个必要条件。

1)互斥条件:指进程对所分配到的资源进行排它性使用,即在一段时间内某资源只由一个进程占用。如果此时还有其它进程请求资源,则请求者只能等待,直至占有资源的进程用毕释放。

2)请求和保持条件:指进程已经保持至少一个资源,但又提出了新的资源请求,而该资源已被其它进程占有,此时请求进程阻塞,但又对自己已获得的其它资源保持不放。

3)不剥夺条件:指进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放。

4)环路等待条件:指在发生死锁时,必然存在一个进程——资源的环形链,即进程集合{P0,P1,P2,···,Pn}中的P0正在等待一个P1占用的资源;P1正在等待P2占用的资源,……,Pn正在等待已被P0占用的资源。

理解了死锁的原因,尤其是产生死锁的四个必要条件,就可以最大可能地避免、预防和解除死锁。

只要打破四个必要条件之一就能有效预防死锁的发生。

打破互斥条件:改造独占性资源为虚拟资源,大部分资源已无法改造。

打破不可抢占条件:当一进程占有一独占性资源后又申请一独占性资源而无法满足,则退出原占有的资源。

打破占有且申请条件:采用资源预先分配策略,即进程运行前申请全部资源,满足则运行,不然就等待,这样就不会占有且申请。

打破循环等待条件:实现资源有序分配策略,对所有设备实现分类编号,所有进程只能采用按序号递增的形式申请资源。

避免死锁常见的算法有有序资源分配法、银行家算法。

ReentrantLock

可以通过其他方法去拿锁

tryLock():尝试拿锁,拿到就往下运行,没拿到就释放锁。

image.png

二、活锁

概念

两个线程在尝试拿锁的机制中,发生多个线程之间互相谦让,不断发生同一个线程总是拿到同一把锁,在尝试拿另一把锁时因为拿不到,而将本来已经持有的锁释放的过程。

解决办法

每个线程休眠随机数,错开拿锁的时间。

三、线程饥饿

概念

低优先级的线程,总是拿不到执行时间(时间片)

解决办法

提高线程优先级。

四、CAS(Compare And Swap)

实现流程

1.先获取变量的值(旧值)
2.执行计算得到新的结果。
3.比较内存中的值和旧值是否相等。
4.相等则和内存数据进行交换。
5.不相等,重新操作,直到成功为止。

其中比较和交换操作是原子操作:要么都执行,要么都不执行。

image.png

由此原理可以去区分一下悲观锁和乐观锁

  • 悲观锁:Synchroized,获得锁才能进行后续操作。
  • 乐观锁:我先进行操作,再去判断可不可以写入内存。

问题

  • ABA问题:中间已经发生了操作,只不过又变回之前的值。 版本戳
  • 开销问题:线程一直自旋,循环。
  • 只能对一个共享变量进行原子操作。(一个地址)