如何解决是否可以显式使用Java内部锁?
在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 个对象的锁的智慧。
解决方法
这在普通的Java代码中是不可能的¹,但是可以在字节码级别上完成。
但是,这样做是不值得的,因为它不能解决递归方法(堆栈消耗)的问题,这似乎是您唯一关心的问题。
这在Does synchronized block have max reentrant limit?中已得到详细说明,在this answer中的示例程序演示了在循环中获取对象监视器的字节码在可用堆栈空间和可以获取的最大监视器获取数之间表现出直接的关系。
换句话说,即使是在循环中获取对象监视器的手工字节码也消耗O(n)堆栈,并且对于大型数组可能会失败,就像递归方法一样。
链接答案的示例代码以循环的方式获取了同一对象的监视器,但是即使没有对其进行优化,也没有理由假设获取不同对象的监视器可以以更少的堆栈空间摆脱困境。
关于您的“奖励问题”,无阻塞的“尝试监控器”没有字节码操作。 sun.misc.Unsafe
的某些版本具有tryMonitorEnter
方法,但这不符合任何标准。
关于结构锁定要求you mentioned in a comment,这没有问题。它仅要求在方法退出时,该方法不得持有其在进入时尚未持有的任何固有锁。因此,如果您的方法在一个循环中获取所有监视器,执行关键部分,然后在一个循环中释放相同的监视器,那么这在形式上是正确的。那是假设数组永远不会在中间被修改。
但是,如上所述,由于JVM不会在运行时优化此类不常见的代码,并且每次获取仍然占用堆栈空间,因此精心设计此类字节码几乎没有好处。
¹很好,提到了sun.misc.Unsafe
,但是使用它的代码不算作“普通Java代码”,甚至比讨论的手工字节码的移植性还差。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。