Project Coin: the try-with-resources lock support debate
Mar 28
Java 7 AutoCloseable, JDK7, lock support, Project Coin, try-with-resources, TWR 6 Comments
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?
RSS
Twitter
Email
Facebook
Join our Early Access Program, read chapters now!
Mar 29, 2011 @ 12:41:04
Hello. Automatic TWR lock support might be useful to me.
But instead of a method generating a new object for each lock, could you use pooling to avoid that much obejct creation? You’d return the instance on Autocloseable.close() to the pool, create more objects when needed?
Regards,
Leo.
Mar 30, 2011 @ 11:49:46
It seems to me Leo’s suggestion pretty much hits the nail on the head; creating of new objects is not the concern of the autoclose logic. Doesn’t the same go for autoclose JDBC objects in the same construct? They are either created or fetched from a pool.
Apr 07, 2011 @ 18:26:33
I honestly don’t see too much value in this. What we’re describing here is synchronized(o).
I’ve written plenty of code with the raw locking constructs, but really most developers should never touch those classes. (Most developers don’t understand locking well enough to do so safely.) Only if you’re writing libraries or special concurrent data structures do you really need to do your own locking instead of using the higher-level constructs already available to you.
I see a lot of value in the auto-close functionality for files and connections. Closing a jdbc connection properly is unjustifiably complicated. But with a lock, it’s just lock try {} finally { unlock }. And if all your methods just use this pattern, you should have just use synchronized.
So I’m not against it, but I can’t really see making the parser more complicated to deal with this. Honestly we should be directing most of our programming toward worker pools and actors, not shared-memory concurrency constructs.
Apr 07, 2011 @ 21:00:39
Hi Sarah,
Some interesting perspectives – that’s what I like about the open development of the Project Coin features – we get to hear from a wider range of viewpoints. Thank you for contributing.
I think I agree with you to a certain degree – most people should be discouraged from writing their own locking implementations. However, some people do have a need to do just that – and I think that doing some analysis on figuring out how widespread that is, in order to justify the cost of adding in additional complexity – I think that’s a sensible approach.
Where I think we differ is that I wouldn’t necessarily advocate an actor model for Java – with mutable state still in play, I think the potential gains for Java are smaller than they might appear. Broadly, and this is of course just my personal opinion, the more I use Clojure’s approach to state and concurrency, the more convinced I am that it’s superior to anything else I’ve seen on the JVM.
The comparison between Clojure agents and Scala actors is the place where I find this to be the most apparent, but the same would of course apply to any mutable state actor scheme in Java.
May 04, 2012 @ 05:19:52
I am not sure how the developers are adopting the new changes. I still see people work in the Java 5.0 versions and not using many of the new features.
http://www.javabeat.net/examples/2012/05/02/whats-new-in-java-7-features-as-part-of-project-coin/
Jun 15, 2012 @ 00:40:51
What’s wrong with:
public class Locker
implements AutoCloseable
{
public Locker( final Lock lock )
{
this.lock = lock;
}
public Locker lock()
{
lock.lock();
return ( this );
}
@Override
public void close()
{
unlock();
}
void unlock()
{
lock.unlock();
}
private final Lock lock;
}
You can then do something like:
private static final Locker someLocker = new Locker( someLock );
(where someLock is a private static Lock) and then
try ( Locker locker = someLocker.lock() )
{
…
}
to avoid having extra object constructions or more lazily:
try ( Locker locker = new Locker( someLock ).lock() )
{
…
}
if you don’t care about the extra object.
Sure it’s a bit messy, but still better than:
lock.lock();
try
{
…
}
finally
{
lock.unlock();
}
Perhaps LockHolder is a better name than Locker…
Perhaps holdLock() is a better method name than lock() here…