14

在高度并发的系统中,很难确信您对锁的使用是正确的。具体来说,如果在另一个线程中以正确的顺序获取锁时,如果以非预期的顺序获取锁,则可能导致死锁。

有一些工具(例如 Coverity)可以对代码库进行静态分析并寻找“不寻常的”锁定顺序。我想探索其他选项来满足我的需求。

是否有任何用于检测 Java 代码的轻量级*工具,可以检测以非预期顺序获取锁的情况?我可以通过注释/注释明确调用锁定命令。

首选免费和/或开源解决方案。如果有解决此问题的非仪器方法,也请发表评论。

* 就我而言,轻量级意味着...

  • 如果是仪表,我仍然可以以相同的性能运行我的程序。我想 30-50% 的降级是可以接受的。
  • 我不必花半天时间与该工具进行交互来获得“好的”。理想情况下,我应该只在出现问题时才注意到我正在使用它。
  • 如果是仪表,那么在生产环境中应该很容易禁用。
  • synchronize它不应该在每条语句中都弄乱我的代码。如前所述,我可以明确地注释/注释被相对顺序锁定的对象或对象类。
4

3 回答 3

3

我没有使用过 AspectJ,所以不能保证它的易用性。我使用ASM创建了一个自定义代码分析器,这大约需要 2 天的时间。仪器同步的工作应该是相似的。一旦你熟悉了方面,AspectJ 应该会更快更容易。

我已经为我们基于 c++ 的服务器实现了死锁检测跟踪。这是我的做法:

  • 每当获取或释放锁时,我跟踪过:
    • <time> <tid> <lockid> <acquiring|releasing> <location in code>
  • 这个额外的痕迹非常严重地影响了性能,并且在生产中不可用。
  • 因此,当在生产中发现可能的死锁时,我使用日志文件来确定死锁周围发生了什么。然后在我的跟踪打开的测试环境中重现了这个功能。
  • 然后我在日志文件上运行了一个脚本,看看是否可能出现死锁以及如何死锁。我使用了一个 awk 脚本,使用了这个算法:
    • Foreach 线
      • 如果获得
        • 将 lockid 添加到该线程的当前锁列表中
        • 将此列表中的每一对锁添加到该线程的一组锁对中。例如Lock A -> Lock B -> Lock C生成对的列表(Lock A, Lock B), (Lock A, Lock C), (Lock B, Lock C)
      • 如果释放
        • 从此线程的列表尾部删除当前的 lockid
    • 对于每个锁对在所有其他线程中搜索反向锁对,每个匹配都是潜在的死锁,因此打印受影响的锁对和线程
    • 然后我没有让算法更智能,而是检查了锁定获取是否是真正的死锁。

在几天没有找到死锁的原因后,我这样做了,又花了几天时间来实施,又花了几个小时才找到死锁。

如果您正在考虑在 Java 中使用这种方法,则需要考虑的是:

  • 您是否仅用于synchronized保护您的关键部分?你在使用 java.lang.concurrent 中的类吗?(这些可能需要特殊处理/仪器)
  • 使用切面/ASM 打印代码位置有多容易?我在 C++ 中使用__FILE__和。__LINE__ASM 会给你类名、方法名和签名。
  • 您无法检测用于保护您的跟踪/日志记录的锁。
  • 如果您使用每个线程的日志文件和文件对象的线程本地存储,您可以简化您的检测。
  • 您如何唯一标识您同步的对象?也许 toString() 和 System.identityHashCode() 就足够了,但可能需要更多。我在 C++ 中使用了对象的地址。
于 2010-10-29T10:44:18.980 回答
1

您可以使用AspectJ,它相对容易学习,并且允许您设置自己的自定义和简化的方式来监控您的线程和它们访问的任何锁。

于 2010-10-29T01:41:16.840 回答
0

不会让您一路走好,但一个好的开始是使用JCIP 注释,并且FindBugs捕获了一些东西。

于 2010-10-28T23:53:00.127 回答