Enrich the Lock interface
David Holmes
david.holmes at oracle.com
Tue Aug 22 05:21:51 UTC 2023
On 21/08/2023 11:56 pm, Albert Attard wrote:
> Hello.
>
> Thank you very much Pavel for helping me find more information about this.
>
> After some research (mainly by Pavel) we found a thread, with subject
> "/Default Functions for Lock Interface/", from October 2013 that
> discussed this exact thing. I don't believe that this thread is
> available on the internet, but I may be wrong.
>
> In this thread, two approaches were discussed.
>
> 1. Add support for the try-with-resources
> 2. Add default methods to the Lock interface
>
>
> The second approach makes use of lambda functions which incur
> performance cost when data is modified within the lambda function, as
> described by Brian Goetz in the same thread (on the 8th of October 2013).
>
> ---
> /When you write a method like
>
> void withLock(Runnable)
>
> the Runnable is going to have side-effects. So its going to be a
> capturing lambda -- one that captures variables from its scope:
>
> withLock( () -> { counter++; } ); // counter is captured
>
> With the current implementation of lambda, evaluating (not invoking) a
> capturing lambda expression will cause an allocation. Whereas the
> hand-unrolled version:
>
> lock.lock();
> try { counter++; }
> finally { lock.unlock(); }
>
> does not. The sort of things people do with locks are generally pretty
> performance-sensitive, so such an API was deemed, at the current time,
> to be an "attractive nuisance."
> /---
>
> Almost 10 years have passed since this was originally suggested. Have
> enough things changed since then, that would make this approach feasible?
Long before lambda's Doug Lea originally had this in the form of
LockedExecutor:
https://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/LockedExecutor.html
but it never made the cut into java.util.concurrent (JSR-166).
A "withLock" style of programming does have some appeal to some people
but it was never considered worthy of a place in the core API. The basic
thing is trivial but then there are issues about returning values, or
specialized exception handling - all of which are best handled, and
easily handled, by application logic.
Cheers,
David
-----
> With kind regards,
> Albert Attard
>
>
> On Mon, 21 Aug 2023 at 15:33, John Hendrikx <john.hendrikx at gmail.com
> <mailto:john.hendrikx at gmail.com>> wrote:
>
> I couldn't find a discussion on openjdk, but for those interested (and
> to save others some searching) there is a JBS ticket:
>
> https://bugs.openjdk.org/browse/JDK-8025597
> <https://bugs.openjdk.org/browse/JDK-8025597>
>
> --John
>
> On 21/08/2023 14:37, Pavel Rappo wrote:
> > This is suggested every once in a while. I appreciate that
> openjdk mailing lists are not easily searchable, but with a bit of
> skill, you could find a few previous discussions on the topic.
> >
> > This has also been discussed on concurrency-interest (at
> cs.oswego.edu <http://cs.oswego.edu> <http://cs.oswego.edu/
> <http://cs.oswego.edu/>>), a dedicated mailing list for concurrency
> in Java. Sadly, that list has been defunct for quite some time now.
> >
> > -Pavel
> >
> >> On 21 Aug 2023, at 13:18, Albert Attard <albertattard at gmail.com
> <mailto:albertattard at gmail.com>> wrote:
> >>
> >> Hello.
> >>
> >> I hope all is well.
> >>
> >> Do you believe it is a bad idea to enrich the Lock interface
> with a set of default methods that safely release the lock once ready?
> >>
> >> Consider the following (dangerous) example.
> >>
> >> final Lock lock = new ReentrantLock ();
> >> lock.lock();
> >> /* Code that may throw an exception */
> >> lock.unlock();
> >>
> >> This example will never release the lock if an exception is
> thrown, as the programmer didn’t wrap this up in a try/finally.
> >>
> >> Adding a default method within the Lock interface, called
> withLock(Runnable) for example or any better name, would streamline
> this, as shown next.
> >>
> >> default void withLock(final Runnable runnable) {
> >> requireNonNull(runnable, "Cannot run a null");
> >> lock();
> >> try {
> >> runnable.run();
> >> } finally {
> >> unlock();
> >> }
> >> }
> >>
> >> The caller can now simply change the above example into the
> following, without having to worry about this.
> >>
> >> final Lock lock = new ReentrantLock ();
> >> lock.withLock(() -> {
> >> /* Code that may throw an exception */
> >> });
> >>
> >> We can have more variants of these default methods, as shown next.
> >>
> >> default <T> T getWithLock(final Supplier<T> supplier) {
> >> requireNonNull(supplier, "The supplier cannot be null");
> >> lock();
> >> try {
> >> return supplier.get();
> >> } finally {
> >> unlock();
> >> }
> >> }
> >>
> >> Any thoughts?
> >>
> >> With kind regards,
> >> Albert Attard
>
More information about the core-libs-dev
mailing list