If you have recently been following Project Coin’s mailing list , you will have spotted an interesting discussion regarding the inclusion of lock management inside the scope of a try-with-resources (TWR) code block.
The Initial Idea
The idea was initiated by Gernot Neppert who proposed a class java.util.concurrent.AutoLockable
which would implement AutoCloseable
, and therefore be eligible for usage with TWR:
package java.util.concurrent.locks;
public abstract class AutoLockable implements AutoCloseable
{
public static AutoLockable locked(final Lock lock)
{
lock.lock();
return new AutoLockable()
{
@Override
public void close() {
lock.unlock();
}
};
}
public abstract void close();
}
Its usage would be something like this:
try(AutoLockable locked = AutoLockable.locked(lock))
{
// Do something in locked scope
}
This proposed idea certainly has merit, however the sharp eyed amongst you will notice a performance issue in creating a new object each time you perform a lock. Additionally, you would also have to have an extra method to acquire the lock (the locked(...)
method in the above example). So what else could be tried?
An Alternative Idea
Dr Heinz M. Kabutz posted a link to a newsletter where he describes an interesting way of using JDK 7′s TWR to automatically unlock Java 5 locks. Long story short, the main idea is to build a wrapper class around a Lock
, which implements AutoCloseable
and manually unlocks in the close()
method. The wrapper along with usage of static imports would bring verbose code like:
lock.lock();
try
{
printLockStatus();
}
finally
{
lock.unlock();
}
… to a more readable form:
try (lock(lock))
{
printLockStatus();
}
However, as the author himself mentioned, the problem of creating extra objects every time you perform a lock still remains. Moreover, as David Holmes signaled in the mailing list, the spec has been updated to prohibit usage of resources as general expressions in the TWR argument and only allow passing them with an accompanying explicit variable declaration.
Why TWR (as it stands) can’t support this
Among the reasons for this update were difficulties encountered by the parser which was not always able to distinguish between the start of an expression and the start of a type:
try(i < j // Ternary operator on variables i and j
? new Resource1() :
new Resource2()) {...}
… compared to code like
try(Box < Resource // Simple generic wrapper around a resource
> resourceBox = Box<>(new Resource1())) {...}
Another reason for change was the use case of managing an existing variable which changes its value inside the try
block. The example presented on the update site is the following:
public class TwrExamples implements AutoCloseable
{
public static void main(String... args)
{
TwrExamples twrEg1 = new TwrExamples();
System.out.println(twrEg1.hashCode());
try(twrEg1)
{
twrEg1 = new TwrExamples(); // Mutating the variable!
System.out.println(twrEg1.hashCode());
}
}
@Override
public void close()
{
System.out.println(hashCode());
}
}
.. where close()
will be called on the first twrEg1
instance, not on the one pointed to at the time the try
block finishes. Thus, output results after running a TWR having a TwrExamples resource as argument, may look like this:
1607576787
1051296202
1607576787
The documentation of the Project Coin features posted on March 1st 2011 by Joe Darcy further strengthens the rule of requiring variables to declare a resource in detriment of using general expressions:
“A resource specification declares one or more local variables; the type of each variable must be a subtype of AutoCloseable or a compile-time error occurs.”
However, assigning a new variable to an already existing one referring to the lock just because TWR prohibits identifiers seems troublesome. There is a section in the JSR 334 spec changelog stating that:
“One potential future extension is to allow a resource to be specified as an expression that is a final or effectively final variable. Such a restricted expression form would remove the need to declare a resource variable for the sole purpose of aliasing an existing variable while avoiding pathologies stemming from the resource variable being modified within the body of the |try|-with-resources statement.”
Joe Darcy states in the mailing list that even though this change is too late to be applied to JDK 7, it would be a nice addition to JDK 8.
Some Extra Analysis
Another heads-up in this direction is related to constructs such as try (Resource r = getResource())
, where methods such as getResource()
are not expected to fail. Some workarounds are proposed by Reinier Zwitserloot in coin-dev, for example not considering the expression Resource r = getResource()
as part of the try
, and do something like this try (Resource r = ...) { try { ..... }}
. Another solution would be setting r
to null
if the exception that triggers the catch
block is resulted from getResource()
method. The example from the mailing list is :
try (Resource r = getResource())
{
/*.... */
}
catch (Exception e)
{
icon = Icons.DEFAULT_ICON;
logger.log("App icon resource " + r.getURL() + " is not available", e);
}
In this case we would prefer obtaining a NullPointerException
in the log, rather than a core dump.
Your Thoughts?
We’re curious to find out any opinions the readers of this blog might have regarding this ongoing topic discussion on coin-dev. Do you think automatic TWR lock support might be useful to you?
This story, "Project Coin: the try-with-resources lock support debate" was originally published by JavaWorld.