A bit of sugar for j.u.c.locks with try-with-resources?
forax at univ-mlv.fr
forax at univ-mlv.fr
Sat Sep 3 17:54:23 UTC 2016
> De: "Dr Heinz M. Kabutz" <heinz at javaspecialists.eu>
> À: "Remi Forax" <forax at univ-mlv.fr>
> Cc: "Ivan Gerasimov" <ivan.gerasimov at oracle.com>, "core-libs-dev"
> <core-libs-dev at openjdk.java.net>
> Envoyé: Samedi 3 Septembre 2016 19:10:19
> Objet: Re: A bit of sugar for j.u.c.locks with try-with-resources?
> Not sure why the discussion keeps on going to EA with try-with-resource with
> locks.
> 1. There is no reason to construct objects, for example just write this class:
> import java.util.concurrent.locks.*;
> public class AutoLock implements AutoCloseable {
> private final Lock lock;
> public AutoLock(Lock lock) {
> this.lock = lock;
> }
> public AutoLock lock() {
> lock.lock();
> return this;
> }
> public void close() {
> lock.unlock();
> }
> }
> And then use it as such:
> import java.util.concurrent.locks.*;
> public class AutoLockTest {
> private final Lock lock = new ReentrantLock();
> private final AutoLock al = new AutoLock(lock);
> public void doSomething() {
> try (AutoLock temp = al.lock()) {
> // do something on the state
> }
> }
> }
> No objects are constructed.
yes, you still pay the cost of an extra field in the class, an extra indirection when calling lock and unlock and an extra nullcheck (this one is hidden in the implementation of a try-with-resources).
You may answer that ReentrantLock already pay the cost of an indirection over an AbstractQueuedSynchronizer (because a lock can be fair or unfair) so i suppose that an unfair implementation of AutoLock that directly extends AbstractQueuedSynchronizer should be faster or as fast as a ReentrantLock.
> 2. No one uses ReentrantLock anyway (unless they absolutely have to). See the
> comment in Java 9 CopyOnWriteArrayList :-)
> /**
> * The lock protecting all mutators. (We have a mild preference
> * for builtin monitors over ReentrantLock when either will do.)
> */
> final transient Object lock = new Object();
yes, synchronized is more versatile until you have contention, want fairness, a tryLock or several Conditions.
> 3. If you were to construct objects in the TWR block, they would be escaped in
> my experience, except in the one case where there are multiple threads trying
> to lock at the same time. However, in that case, ReentrantLock will anyway
> create objects to manage the contention. But it really is a non-issue, since it
> is trivial to code to not construct any objects with the TWR block in the first
> place.
> I keep getting asked this question all over the world. Personally I think it's a
> non-issue, since the code is so trivial to write yourself, plus so few people
> would use ReentrantLock anyway.
> Regards
regards,
Rémi
> Heinz
> --
> Dr Heinz M. Kabutz (PhD CompSci)
> Author of "The Java(tm) Specialists' Newsletter"
> Sun/Oracle Java Champion since 2005
> JavaOne Rock Star Speaker 2012 http://www.javaspecialists.eu Tel: +30 69 75 595
> 262
> Skype: kabutz
> Remi Forax wrote:
>> ----- Mail original -----
>>> De: "Ivan Gerasimov" <ivan.gerasimov at oracle.com> À: "Krystal Mok"
>>> <rednaxelafx at gmail.com> , "core-libs-dev" <core-libs-dev at openjdk.java.net>
>>> Envoyé: Samedi 3 Septembre 2016 12:23:28
>>> Objet: Re: A bit of sugar for j.u.c.locks with try-with-resources?
>>> Hi Krystal!
>>> On 03.09.2016 5:41, Krystal Mok wrote:
>>>> Hi core-libs developers,
>>>> I mostly live down in the VM world, but recently I've been playing with
>>>> j.u.c.locks a bit, and saw that there's an opportunity to retrofit the API
>>>> with the try-with-resources syntax. I wonder if anybody has brought this
>>>> topic up before; apologies if there had been.
>>>> >From the JavaDoc of j.u.c.l.ReentrantLock, the following is a typical usage:
>>>> class X {
>>>> private final ReentrantLock lock = new ReentrantLock();
>>>> // ...
>>>> public void m() {
>>>> lock.lock(); // block until condition holds
>>>> try {
>>>> // ... method body
>>>> } finally {
>>>> lock.unlock()
>>>> }
>>>> }
>>>> }
>>> It could be written as
>>> public void m() {
>>> lock.lock(); // block until condition holds
>>> try (Closeable unlocker = lock::unlock) {
>>> // ... method body
>>> }
>>> }
>>> This would save a couple of lines of code.
>> but it does an allocation (if the escape analysis fails),
>> i think it's better to wait for proper value types :)
>>> With kind regards,
>>> Ivan
>> regards,
>> Rémi
>>>> The try...finally construction really pops out as a try-with-resources
>>>> candidate.
>>>> So what if we retrofit that with something like:
>>>> class X {
>>>> private final ReentrantLock lock = new ReentrantLock();
>>>> // ...
>>>> public void m() {
>>>> try (lock.lock()) { // block until condition holds
>>>> // ... method body
>>>> } // automatic unlock at the end
>>>> }
>>>> }
>>>> Assuming lock.lock() returns a temporary wrapper object (let's call it a
>>>> "Locker" for this discussion), where Locker implements AutoCloseable, and
>>>> its close() method calls lock.unlock().
>>>> That'll make the API look and feel quite similar to the built-in
>>>> "synchronized () { ... }" syntax. With escape analysis and scalar
>>>> replacement implemented correctly in the VM, this temporary Locker object
>>>> wouldn't incur much (or any) runtime cost after optimized JIT'ing, so it
>>>> feels like a pure win to me.
>>>> What do you think?
>>>> Best regards,
>>>> Kris (OpenJDK username: kmo)
More information about the core-libs-dev
mailing list