xiaoyh 的个人博客

一个只会敲代码的咸鱼

0%

面试 —— 死锁

Java 中的死锁

Java 中的死锁主要是线程死锁,是指由于两个或者多个线程互相持有对方所需要的资源,导致这些线程处于等待状态,无法前往执行。

当线程进入对象的 synchronized 代码块时,便占有了资源,直到它退出该代码块或者调用 wait 方法,才释放资源。在此期间,其他线程将不能进入该代码块。

当线程互相持有对方所需的资源时,会互相等待对方释放资源,如果线程都不主动释放所占有的资源,将产生死锁。

产生原因

若干线程之间形成一种头尾相接的循环等待资源关系。

如何避免死锁

  1. 加锁顺序:确保线程都是按照相同的顺序获得锁,但这个一般很难实现;
  2. 加锁时限:在尝试获取锁的时候加一个超时时间,若一个线程没有在给定的时限内成功获得需要的锁,则会进行回退并释放所有已经获得的锁,然后等待一段随机的时间再重试;
  3. 死锁检测:每当一个线程获得了锁,会在线程和锁相关的数据结构中(map、graph等等)将其记下。除此之外,每当有线程请求锁,也需要记录在这个数据结构中。当一个线程请求锁失败时,这个线程可以遍历锁的关系图看看是否有死锁发生。检测到死锁后,可以让优先级较低的线程回退。

MySQL 中的死锁

MySQL 中的死锁是由事务之间互相持有对方所需的锁造成的,比如事务 A 要更新的数据的 id 列表为[1, 2, 3, 4],事务 B 要更新的数据的 id 列表为[8, 9, 10, 4, 2],这样就造成了死锁。

如何避免

  1. 以固定的顺序访问表和行;
  2. 如果业务允许,将大事务拆小;
  3. 酌情降低隔离级别;
  4. 为表添加合理的索引,因为不走索引将会为表的每一行记录添加上锁,死锁的概率大大增大。