可以明确使用Java内部锁吗?

huangapple go评论77阅读模式
英文:

Is it possible to use Java intrinsic locks explicitly?

问题

在Java中,假设你有一个数组:

Object[] objs = {o1, o2, ... , oN};

以及一个关键部分:

{
 critical();
}

并且你希望在持有objs中每个元素的内部锁的情况下执行关键部分。

我可以想到一种方法来实现这个,它涉及对递归的可怕滥用:

void syncArray(int i) {
  if (i >= 0) {
    synchronized(objs[i]) {
       syncArray(i - 1);
    }
  } else {
    critical();
  }
}

syncArray(objs.length - 1);

除了不美观之外,这涉及O(N)的堆栈帧,这可能不是一个好选择。是否有更好的方法?我真正想要的是一种在没有使用synchronized关键字的情况下获取和释放内部锁的方法。如果你有一种非阻塞的方式来尝试获取内部锁,那就更好了。

注意:我不是在询问这是否是一个好主意(它不是),只是在询问是否可能。在现实世界中,显然是要使用显式锁,并且还要深思熟虑一下一次性为N个对象获取锁的智慧。

英文:

In Java, suppose you have an array:

Object[] objs = {o1, o2, ... , oN};

And a critical section:

{
 critical();
}

And you wish to execute the critical section while holding the intrinsic lock of every element in objs.

I can think of one way to do this, and it involves an abominable abuse of recursion:

void syncArray(int i) {
  if (i >= 0) {
    synchronized(objs[i]) {
       syncArray(i - 1);
    }
  } else {
    critical();
  }
}

syncArray(objs.length - 1);

Besides being ugly, this entails O(N) stack frames, which is probably not great. Is there a better way? What I really want is a way to acquire and release intrinsic locks without the synchronized keyword. Bonus points if you have a non-blocking way to attempt to acquire an intrinsic lock.

Note: I'm not asking if this is a good idea (it's not), just if it's possible. The real-world answer is obviously just to use explicit locks and also do some soul-searching about the wisdom of trying to acquire locks for N objects at once.

答案1

得分: 2

这在普通的Java代码中是不可能的,但可以在字节码级别完成。

然而,这样做不值得,因为它无法解决您递归方法的问题,即栈消耗,这似乎是您唯一关心的问题。

这已经在https://stackoverflow.com/q/54897677/2711488中有详细说明。此答案中的示例程序演示了在循环中获取对象监视器的字节码与可用堆栈空间之间存在直接关系,以及您可以进行的最大监视器获取次数。

换句话说,即使是手工制作的在循环中获取对象监视器的字节码也具有O(n)的堆栈消耗,并且对于更大的数组可能与您的递归方法一样失败。

链接答案的示例代码在循环中获取相同对象的监视器,但即使如此,也没有理由认为获取不同对象的监视器可以消耗更少的堆栈空间。

关于您的“奖励问题”,没有用于非阻塞“try‑monitorenter”的字节码操作。一些版本的sun.misc.Unsafe有一个tryMonitorEnter方法,但这超出了任何标准。

关于您提到的“结构化锁定”要求,这不会造成问题。它只要求在方法退出时,该方法不得持有任何在进入时尚未持有的内部锁。因此,如果您的方法在循环中获取所有监视器,执行关键部分,然后在循环中释放相同的监视器,那么它将是正式正确的。前提是数组在此期间没有被修改。

但正如前面所说,制作这样的字节码几乎没有任何好处,因为JVM不会在运行时优化这种不常见的代码,并且仍然会为每次获取消耗堆栈空间。


¹好吧,提到了sun.misc.Unsafe,但使用它的代码不算“普通的Java代码”,而且甚至比讨论的手工制作的字节码更不具有可移植性。

英文:

This is not possible in ordinary Java code¹, but could be done on the bytecode level.

However, doing so would not be worth the effort, as it doesn’t solve the issue of your recursive approach, the stack consumption, which seems to be your only concern.

This has been elaborated in https://stackoverflow.com/q/54897677/2711488 The example program in this answer demonstrates that bytecode acquiring an object monitor in a loop exhibits a direct relationship between available stack space and maximum number of monitor acquisitions you can make.

In other words, even the handcrafted bytecode acquiring the object monitors in a loop has an O(n) stack consumption and may fail for larger arrays the same way as your recursive approach.

The example code of the linked answer acquires the monitor of the same object in a loop but if even that doesn’t get optimized, there is no reason to assume that acquiring monitors of different objects could get away with less stack space.

Regarding your “bonus question”, there is no bytecode operation for a non-blocking “try‑monitorenter”. Some versions of sun.misc.Unsafe have a tryMonitorEnter method, but that is out of any standard.

Regarding the structured locking requirement you mentioned in a comment, this imposes no problem. It only requires that upon method exit, the method must not hold any intrinsic locks it didn’t already hold at entry. So if you method acquires all monitors in a loop, executes the critical section, and releases the same monitors in a loop, it would be formally correct. That is assuming that the array never gets modified in-between.

But as said, there is little gain in crafting such bytecode, as the JVM won’t optimize such uncommon code at runtime and still consumes stack space for every acquisition.


¹ well, there’s the mentioned sun.misc.Unsafe, but code using it doesn’t count as “ordinary Java code” and is even less portable than the discussed handcrafted bytecode.

huangapple
  • 本文由 发表于 2020年8月14日 10:22:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/63405626.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定