Java 中同步顺序的断言

发布于 2024-09-29 19:35:11 字数 554 浏览 4 评论 0原文

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

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

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

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

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

  • 如果是仪器,我仍然可以以相同的大概性能运行我的程序。我认为 30-50% 的退化是可以接受的。
  • 我不必花半天时间与该工具交互只是为了得到一个“好的”结果。理想情况下,我应该只在出现问题时才注意到我正在使用它。
  • 如果是仪器,那么在生产环境中应该很容易禁用它。
  • 它不应该在每个 synchronize 语句中使我的代码变得混乱。如前所述,我可以明确地注释/注释通过相对顺序锁定的对象或对象类。

In highly concurrent systems, it can be difficult to be confident that your usage of locks is correct. Specifically, deadlocks can result if locks are acquired in an order that was not expected while being acquired in the proper order in another thread.

There are tools (e.g. Coverity) which can do static analysis on a code base and look for "unusual" locking orders. I'd like to explore other options to meet my needs.

Are there any light-weight* tools for instrumenting Java code which can detect cases where locks are being acquired in an order other than expected? I am okay with explicitly calling out locking orders via comments / annotations.

Free and/or open-source solutions preferred. Please also comment if there are non-instrumentation approaches to this problem.

* For my purposes, light-weight means...

  • If it is instrumentation, I can still run my program with the same ballpark performance. 30-50% degradation is acceptable, I suppose.
  • I don't have to spend half the day interacting with the tool just to get an "okay" out of it. Ideally I should only notice that I'm using it when there's a problem.
  • If it is instrumentation, it should be easy to disable for production environments.
  • It shouldn't clutter my code at every synchronize statement. As previously mentioned, I'm okay with explicitly commenting/annotating the objects or classes of objects which get locked with relative orderings.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

浮生面具三千个 2024-10-06 19:35:12

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

You can use AspectJ, which is relatively easy to learn and will allow you to setup your own customized and simplified way of monitoring your threads and any locks they access.

柠北森屋 2024-10-06 19:35:12

虽然无法让您一路顺利,但一个好的开始是使用 JCIP 注释,以及 FindBugs 捕获了一些内容。

Doesn't get you all the way there, but a good start is to use the JCIP annotations, and FindBugs catches a few things.

回梦 2024-10-06 19:35:11

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

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

  • 当获取或释放锁时,我跟踪:
    • <代码><时间>;<锁定ID> <获取|释放> <代码中的位置>
  • 这个额外的跟踪极大地影响了性能,并且在生产中无法使用。
  • 因此,当在生产中发现可能的死锁时,我使用日志文件来找出死锁周围发生的情况。然后在打开跟踪的测试环境中重现此功能。
  • 然后我在日志文件上运行一个脚本来查看是否可能发生死锁以及如何发生死锁。我使用 awk 脚本,使用以下算法:
    • 每行
      • 如果获取
        • 将 lockid 添加到该线程的当前锁列表
        • 将此列表中的每对锁添加到该线程的一组锁对中。例如,对于 Lock A 列表 ->锁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++中使用了对象的地址。

I have not used AspectJ so cannot vouch for how easy it is to use. I have used ASM to create a custom code profiler, this was about 2 days work. The effort to instrument synchronization should be similar. AspectJ should be quicker and easier once you are up to speed with aspects.

I have implemented deadlock detecting trace for our c++ based server. Here is how I did it:

  • When ever acquiring or releasing a lock I traced:
    • <time> <tid> <lockid> <acquiring|releasing> <location in code>
  • This extra trace affected performance quite drastically and was not usable in production.
  • So when a possible deadlock was discovered in production I used the log file to figure out what was happening around the deadlock. Then reproduced this functionality in a test environment with my tracing turned on.
  • Then I ran a script on the log file to see if deadlock was possible and how. I used an awk script, using this algoritm:
    • Foreach line
      • if acquiring
        • add lockid to list of current locks for this thread
        • add each pair of locks in this list to a set lock pairs for this thread. eg for list of Lock A -> Lock B -> Lock C generate the pairs (Lock A, Lock B), (Lock A, Lock C), (Lock B, Lock C)
      • if releasing
        • remove current lockid from tail of list for this thread
    • For each lock pair search all other threads for the reverse lock pairs, each match is a potential deadlock so print the pairs and threads affected
    • Instead of making the algorithm smarter I then desk checked that the lock acquisition to see if it was a real deadlock.

I did this after failing to find the cause of a deadlock for a number of days, it took a few more days to implement and a few hours to find the deadlock.

If you are considering this approach in Java things to consider are:

  • Do you only use synchronized to protect your critical sections? Are you using the classes in java.lang.concurrent? (these might require special handling/instrumentation)
  • How easy is it to print the code location with aspects/ASM? I used __FILE__ and __LINE__ in c++. ASM will give you the class name, method name and signature.
  • You cannot instrument the locks used to protect your tracing/logging.
  • You can streamline your instrumentation if you use a log file per thread and thread local storage for the file object.
  • How do you uniquely identify objects you synchronize on? Maybe toString() and System.identityHashCode() would be enough, but might require more. I used the address of the object in C++.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文