7

Along the same lines as this question, I wonder why the Java team didn't add a few default methods to the Lock interface, something like this:

public default void withLock(Runnable r) {
  lock();

  try {
     r.run();
  } finally {
     unlock();
  }
}

This would allow a programmer to do

public class ThreadSafe {
  // Object requiring protection
  private final List<String> l = new ArrayList<>();
  private final Lock lock = new ReentrantLock();

  public void mutate(String s) {
    lock.withLock(() -> l.add(s));
  }

  public void threadSafeMethod {
    lock.withLock(() -> { System.out.println(l.get(l.size())); });
  }
}

instead of

public void threadSafeMethod {
  lock.lock();
  try {
    System.out.println(l.get(l.size())); 
  } finally { 
    lock.unlock();
  }
}
Community
  • 1
  • 1
rxg
  • 3,777
  • 22
  • 42

1 Answers1

6

It seems like the answer to this one is similar to the answer to the linked question - it was deemed an 'attractive nuisance', due to the allocation cost of the Runnable created from the lambda (see Brian Goetz's response to the request).

The good news is that there is an open bug for this request, which means that Oracle seems open to revisiting this decision once the JVM can reliably minimise the allocation cost.

Community
  • 1
  • 1
rxg
  • 3,777
  • 22
  • 42
  • 1
    As a side comment, Brian's example `withLock( () -> { counter++; } );` cannot possibly be a capturing lambda because `counter` in that example cannot be a local variable. But we get the point, and actually the limitation that you can't mutate a local var within such a `withLock`, or issue `return` and other early-exit control statements, may be an even stronger argument against it. – Marko Topolnik Jun 04 '14 at 11:15
  • 1
    @Marko Topolnik: the example `() -> counter++` *is* a capturing lambda but it works only if `counter` is a member variable so the lambda remembers the immutable reference to `this` (or the class in case of a `static` variable). Note that if `counter` was a local variable not only the lambda didn’t work, it turned the entire example pointless as there is no reason why an update to a *local* variable should be guarded by a `Lock`. – Holger Jun 04 '14 at 13:49
  • @Holger There seems to be a terminological misalignment: my point was that it is not a *closure* as it doesn't close over local variables (I assumed this was synonymous with "capturing lambda"). This means that the example does not demonstrate any performance issues, therefore does not serve to explain why `withLock` wasn't included in the API. Your additional point that the example would be fruitless if `counter` was a local var reinforces this. – Marko Topolnik Jun 05 '14 at 08:23
  • @Marko Topolnik: Brian Goetz’ explanation targets the issue that with the current lambda implementation a new instance is created for every invocation when the lambda has state ([see my explanation here](http://stackoverflow.com/a/23991339/2711488)). It doesn’t matter whether it captures `this` or `counter`, a lambda useful in this context will always have state and hence produce a new instance every time. So his statement is correct regarding “capturing”. However, I don’t see why this small performance issue should drive API design decisions here when it is ignored for all other APIs… – Holger Jun 05 '14 at 08:42
  • @Holger A reasonable expectation would be that a `this`-capturing lambda would be accessible from the object referred to by `this`, not involving any hashtable-backed lambda cache. Also, I think your use of the term "call site" does not refer to call sites, but rather to lambda creation sites. – Marko Topolnik Jun 05 '14 at 08:50
  • @Marko Topolnik: the term “call site” origins from the `invokedynamic` concept. See, even the object returned by `LambdaMetaFactory` has the type `CallSite`. This is the “lambda creation site” if `invokedynamic` is used for lambda creation. I’m sorry if this creates confusions. As of current lambda implementation, lambda capturing `this` are not cached. And, as said, it’s not even clear whether caching would be any good (let’s say, if the JVM has a good escape analysis). That’s why I don’t understand the reasoning to not include the discussed method. – Holger Jun 05 '14 at 08:53
  • @Holger Escape analysis would be only good for *downward funargs*. Granted, that covers the majority of use cases for `withLock`. Generally we agree that the argumentation is insufficent, especially because those rare cases where performance is of utmost concern could always revert to the "exploded" idiom. – Marko Topolnik Jun 05 '14 at 10:06
  • The linked bug has been closed. Brian writes: "While we're not ruling out the possibility of doing this in the future, that future is far away. Closing because so much has to happen before this becomes a viable approach." – dnault Oct 08 '15 at 21:04