Is it possible to use Java intrinsic locks explicitly?

Viewed 162

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 Answers

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 Does synchronized block have max reentrant limit? 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.

Related