写点什么

如何设计一组会出现死锁 (Deadlock) 的 ABAP 程序

作者:Jerry Wang
  • 2022 年 8 月 11 日
    四川
  • 本文字数:1878 字

    阅读完需:约 6 分钟

如何设计一组会出现死锁(Deadlock)的ABAP程序

我们在计算机操作系统这门专业课上,学过死锁(Deadlock)的概念:两个或两个以上的进程(或线程)在执行过程中,由于竞争资源而造成的一种阻塞的现象,称为死锁。若无外力干预,这些处于死锁状态的进程将永远处于互相等待的阻塞状态中。



正好我儿子走到我电脑前看到文章标题,好奇地问我什么是死锁。我解释道,“假设你和白妹妹(他的玩伴)手上都有一张奥特曼白金卡,你特别想要白妹妹手上那张白金卡,白妹妹也特别想要你手上那张白金卡。你们都想让对方把他/她手上那张卡送给你们,但你们都舍不得把自己手上那张卡送出去。这就是死锁。”


儿子又问,那这种情况咋办。


我说,只有靠大人的介入。比如你老爸出马,把你们手中两张卡都没收了,等我玩够了再还给你们,这样你们就不会死锁了。




言归正传,在使用 ABAP 答这道面试题之前,我们先看看如何用 Java 编写一个会出现死锁的程序。


不到 40 行代码就完成任务。为了便于 ABAP 从业人员理解,没有使用 Java 里的 Lambda 表达式,否则代码可以更短。


该程序逻辑如下:


线程 1 持有资源 1,试图持有资源 2;线程 2 持有资源 2,试图持有资源 1;死锁发生;



现在进入 ABAP 部分。


ABAP 帮助文档提到,使用 SELECT SINGLE FOR UPDATE 读取单条记录时,会自动在数据库层面为该条记录设置一把锁(Exclusive lock,有的中文文档翻译成排他锁)。如果上锁操作会导致死锁发生,则会抛出异常。



select single for update 关键字将获得表上的一个锁,直到开发人员更新该表。


当用 single 读取单行时,可以使用添加的 for UPDATE 为该行设置独占锁。在这种情况下,只有当检查由 AND 链接的逻辑表达式中的所有主键字段,以确保它们在 WHERE 条件中相同时,才使用 SELECT 命令。否则,结果集为空,并且 sy-subrc 被设为 8。如果锁导致死锁,则会发生运行时异常。如果使用了添加的 FOR UPDATE, SELECT 命令将绕过 SAP 缓冲。


学习了这些理论知识后,于是我们首先创建一个 ABAP 数据库表,插入两条记录:



再开发两个 ABAP 程序 ZLOCK1 和 ZLOCK2,分别按照 Z01, Z02 和 Z02, Z01 的顺序使用 SELECT SINGLE FOR UPDATE 向数据库发起读取请求。




开启两个 SAPGUI 窗口,按照下面的步骤执行这两个程序,即可产生死锁。


(1) 以调试模式单步执行程序 ZLOCK1,成功执行完 SELECT SINGLE FOR UPDATE .. WHERE object_id = 'Z01', 意味着此时程序 ZLOCK1 在数据库层面对 Z01 这条记录设置了一把锁。



(2) 切换到另一个 SAPGUI 窗口,执行程序 ZLOCK2, 单步调试执行完语句 SELECT SINGLE FOR UPDATE .. WHERE object_id = 'Z02',即此时程序 ZLOCK2 在数据库层面对 Z02 成功上锁。



(3) 再回到窗口 1,继续调试程序 ZLOCK1,此时调试器会阻塞,因为 ZLOCK1 试图对 Z02 上锁,而此时程序 ZLOCK2 持有记录 Z02 的锁。程序 ZLOCK1 处于阻塞状态,等待 ZLOCK2 释放对记录 Z02 设置的锁。



(3) 回到窗口 2,继续在调试模式下执行 ZLOCK2 程序第 12 行语句。此时程序 ZLOCK2 试图对记录 Z01 上锁,但该记录已经被程序 ZLOCK1 锁住了,程序 ZLOCK2 只好等待程序 ZLOCK1 释放对记录 Z01 的锁;而程序 ZLOCK1 永远也不可能释放对记录 Z01 上的锁,因为此刻它已经处于阻塞状态了,在等待程序 ZLOCK2 释放对记录 Z02 的锁。



此时 ABAP Kernel 检测到程序 ZLOCK2 发生了死锁,抛出一个运行时异常,结束了 ZLOCK2 的执行。程序 ZLOCK2 持有对记录 Z02 的锁也自动释放了。最后的结果是,程序 ZLOCK1 成功地对记录 Z02 上了锁。



在事务码 ST22 里,我们能观察到这次死锁的详情。



ABAP Kernel 这种检测到死锁发生后,立刻终止程序运行的方式,节省了应用开发人员通过阅读代码去分析死锁的时间。


回到 Jerry 之前的 Java 程序,运行之后两个线程陷入死锁,在控制台上没有打印任何可供排错的信息。然而 JDK 本身提供了方便的检查 Java 应用死锁状态的命令行工具:jstack.



命令行执行 jstack,传入 Java 程序的进程 id,如果该程序发生了死锁,该工具会打印出程序里具体是哪一行代码,在试图对何种资源进行上锁操作时出现的死锁,从而帮助开发人员迅速定位到逻辑上存在缺陷的代码位置。



此外,在 ABAP 里,SELECT SINGLE 和 SELECT UP TO 1 ROWS 仍有细微的差别。

  • 如果期望在表格结果中选择具有完全指定键的一行,使用二者均可。

SELECT …
FROM dbtab
WHERE full_key
INTO TABLE itab.
复制代码
  • 如果期望在结果结构体中选择具有完全指定键的一行,使用 SELECT SINGLE:

SELECT SINGLE …
FROM dbtab
WHERE full_key
INTO wa.
复制代码
  • 如果希望选择具有部分指定键的一行,使用 UP TO 1 ROWS:

SELECT …
FROM dbtab
WHERE part_key
ORDER BY …
INTO TABLE itab
UP TO 1 ROWS.
复制代码

希望本文这个小小的例子能帮助大家回忆起死锁这个基础知识点,感谢阅读。


发布于: 刚刚阅读数: 4
用户头像

Jerry Wang

关注

🏆InfoQ写作平台-签约作者🏆 2017.12.03 加入

SAP成都研究院开发专家,SAP社区导师,SAP中国技术大使。2007 年从电子科技大学计算机专业硕士毕业后加入 SAP 成都研究院工作至今。工作中使用 ABAP, Java, JavaScript 和 TypeScript 进行开发。

评论

发布
暂无评论
如何设计一组会出现死锁(Deadlock)的ABAP程序_操作系统_Jerry Wang_InfoQ写作社区