死锁的解决方式

本文深入探讨了数据库死锁现象,包括其定义、必要条件及常见场景。详细介绍了死锁的解决策略,如超时机制、等待图算法、死锁预防、检测与恢复,以及避免策略。通过具体代码示例,展示了如何在实际应用中手动触发死锁。

数据库死锁的解决方式:

死锁是指多个事务在执行时,因争夺锁资源而造成的一种互相等待的现象。解决方法有:

  • 超时机制: 通过参数 Innodb_lock_wait_timeout 来设置超时等待时间,两个事务互相等待时,达到设置的超时等待时间后,其中一个事务进行回滚,这样另一个等待的事务就能继续进行了。优点是简单,缺点是如果事务操作更新了很多行,那么进行回滚会非常占用时间。
  • 等待图: InnoDB 引擎采用的是等待图的方式来进行死锁检测,它保存锁的信息链表和事务等待链表,采用深度优先算法来判断是否存在回路,如果存在,就表示存在死锁,通过将持有最少行级排他锁的事务进行回滚来解决。(回滚 undo 量最小的事务)

什么是死锁

死锁是指多个进程在运行过程中因争夺资源而造成的一种僵局,当进程处于这种僵持状态时,如果没有外力作用,它们都将无法继续向前推进。 比如有一个线程A,按照先锁a再获得锁b的的顺序获得锁,而在此同时又有另外一个线程B,按照先锁b再锁a的顺序获得锁。此时线程a持有第一个锁,等待获取第二个锁,而线程b持有第二个锁,等待获取第一个锁,这样就造成了死锁。

死锁的必要条件

  • 互斥条件:每个资源要么已经分配给了一个进程,要么就是可用的。
  • 请求和保持条件:已经得到了某个资源的进程可以再请求新的资源,并且对已获得的资源保持不放。
  • 不可抢占条件:进程获取到的资源在使用完之前不能被其他线程抢占。
  • 环路等待条件:有两个或者两个以上的进程组成一条环路,该环路中的每个进程都在等待下一个进程所占有的资源。破坏方法:给资源统一编号,进程只能按编号顺序来请求资源。

解决方式

  • 死锁预防: 破坏死锁的必要条件——破坏请求和保持条件:让所有进程在执行前请求所需要的全部资源。破坏环路等待条件:给资源统一编号,进程只能按编号顺序来请求资源。

  • 死锁检测与死锁恢复: 不试图阻止死锁,而是当检测到死锁发生时,采取措施进行恢复,比如抢占恢复、回滚恢复、通过杀死进程来恢复。

  • 死锁避免: 在程序运行时避免发生死锁。(银行家算法)

  • (不推荐)鸵鸟策略: 也就是假装没发生问题,因为解决死锁的代价很高,所以鸵鸟策略会获得很高的性能。在死锁不会对用户造成多大影响,或者发生概率很低时,可以采用鸵鸟策略。

死锁场景

  • 使用 ReentranLock 时未通过 unlock 释放锁。

数据库中的死锁

在这里插入图片描述

手写死锁

public class deadLock {
    private static final Object A = new Object();
    private static final Object B = new Object();

    public static void main(String[] args) {
        Thread threadA = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (A) {
                    System.out.println(Thread.currentThread() + "get sourceA");
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread() + "waiting get sourceB");
                    synchronized (B) {
                        System.out.println(Thread.currentThread() + "get sourceB");
                    }
                }
            }
        });

        Thread threadB = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (B) {
                    System.out.println(Thread.currentThread() + "get sourceB");
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread() + "waiting get sourceA");
                    synchronized (A) {
                        System.out.println(Thread.currentThread() + "get sourceA");
                    }
                }
            }
        });

        threadA.start();
        threadB.start();
    }
}
### 排查和解决程序中死锁方法 #### 什么是死锁死锁是指两个或多个进程在执行过程中因争夺资源而造成的一种僵局状态,其中每个进程都持有某些资源并等待其他进程释放其所需的资源。这种情况下,没有任何一个进程能够继续运行下去。 #### JVM 中的死锁排查方法 为了有效排查 JVM 的死锁问题,可以通过以下方式获取线程转储信息,并分析可能存在的死锁情况: 1. **使用 `jstack` 工具** 可以利用 JDK 自带的 `jstack` 命令来生成线程堆栈信息。该命令会显示当前 Java 进程中所有线程的状态以及它们持有的锁信息。如果存在死锁,则会在输出中标明具体的死锁链路[^1]。 ```bash jstack <pid> ``` 2. **启用 `-XX:+UseDeadLockDetect` 参数** 如果希望 JVM 能够自动检测到死锁,可以在启动应用程序时加入参数 `-XX:+UsePerfData` 和 `-XX:+PrintConcurrentLocks` 来监控并发锁的行为。不过需要注意的是,这种方式可能会带来一定的性能开销。 3. **通过 JConsole 或 VisualVM 图形化界面** 使用这些图形化的管理工具也可以方便地观察线程活动状况及其锁定关系图谱,从而快速定位潜在的死锁风险点。 #### 编码阶段预防措施 为了避免后期诊断带来的麻烦,在编码初期就应该采取相应策略减少发生死锁的可能性: - **保持一致性的加锁顺序** 当多个对象都需要被同步访问的时候,应该遵循固定的加锁次序规则,防止不同路径下交错申请导致循环依赖形成死锁局面[^2]。 - **缩短持有时间** 尽量让临界区内的操作越短越好,这样可以降低其它线程因为长时间等待某个特定资源而陷入阻塞的概率。 - **采用超时机制** 对于那些有可能会长期占用公共资源的操作考虑设置合理的尝试时限,一旦超过指定范围就主动放弃此次请求重新安排后续逻辑处理流程。 #### 数据库层面 (MySQL InnoDB) 处理办法 对于数据库内部产生的事务死锁现象同样值得关注: - **调整隔离级别** 默认 READ COMMITTED 隔离级相对 SERIALIZABLE 更能规避部分不必要的冲突情形;同时合理设计索引结构有助于提升查询效率进而间接缓解压力条件下的竞争态势[^3]。 - **捕获异常重试业务逻辑** 应用层面上应当具备针对 SQLSTATE '40001' 错误代码做出响应的能力——即当发现由引擎返回此类型错误消息时表示刚刚经历了一次失败提交过程,此时可以选择适当延迟后再重复执行相同指令直至成功完成为止。 ```sql BEGIN; -- Your transactional statements here... COMMIT; EXCEPTION WHEN OTHERS THEN IF SQLCODE = -8006 THEN -- Assuming DBMS-specific error code mapping to deadlock. PERFORM pg_sleep(random() * max_delay); -- Randomized backoff strategy. RAISE NOTICE 'Retrying after encountering a deadlock.'; RETURN retry_logic(); ELSE RAISE; -- Re-throw unexpected exceptions. END IF; END; ``` ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值