Java 中的死锁
Java 中的死锁主要是线程死锁,是指由于两个或者多个线程互相持有对方所需要的资源,导致这些线程处于等待状态,无法前往执行。
当线程进入对象的 synchronized 代码块时,便占有了资源,直到它退出该代码块或者调用 wait 方法,才释放资源。在此期间,其他线程将不能进入该代码块。
当线程互相持有对方所需的资源时,会互相等待对方释放资源,如果线程都不主动释放所占有的资源,将产生死锁。
产生原因
若干线程之间形成一种头尾相接的循环等待资源关系。
如何避免死锁
- 加锁顺序:确保线程都是按照相同的顺序获得锁,但这个一般很难实现;
- 加锁时限:在尝试获取锁的时候加一个超时时间,若一个线程没有在给定的时限内成功获得需要的锁,则会进行回退并释放所有已经获得的锁,然后等待一段随机的时间再重试;
- 死锁检测:每当一个线程获得了锁,会在线程和锁相关的数据结构中(map、graph等等)将其记下。除此之外,每当有线程请求锁,也需要记录在这个数据结构中。当一个线程请求锁失败时,这个线程可以遍历锁的关系图看看是否有死锁发生。检测到死锁后,可以让优先级较低的线程回退。
MySQL 中的死锁
MySQL 中的死锁是由事务之间互相持有对方所需的锁造成的,比如事务 A 要更新的数据的 id 列表为[1, 2, 3, 4],事务 B 要更新的数据的 id 列表为[8, 9, 10, 4, 2],这样就造成了死锁。
如何避免
- 以固定的顺序访问表和行;
- 如果业务允许,将大事务拆小;
- 酌情降低隔离级别;
- 为表添加合理的索引,因为不走索引将会为表的每一行记录添加上锁,死锁的概率大大增大。