48

Locks自動クローズ可能ですか?つまり、次の代わりに:

Lock someLock = new ReentrantLock();
someLock.lock();
try
{
    // ...
}
finally
{
    someLock.unlock();
}

...言ってもいい:

try (Lock someLock = new ReentrantLock())
{
    someLock.lock();
    // ...
}

... Java 7では?

4

12 に答える 12

61

私はこれを自分で行うことを検討していて、次のようなことをしました:

public class CloseableReentrantLock extends ReentrantLock implements AutoCloseable { 
   public CloseableReentrantLock open() { 
      this.lock();
      return this;
   }

   @Override
   public void close() {
      this.unlock();
   }
}

次に、これをクラスの使用法として使用します。

public class MyClass {
   private final CloseableReentrantLock lock = new CloseableReentrantLock();

   public void myMethod() {
      try(CloseableReentrantLock closeableLock = lock.open()) {
         // locked stuff
      }
   }
}
于 2012-06-12T16:04:29.313 に答える
27

いいえ、Lockインターフェイス(またはReentrantLockクラス)のどちらもAutoCloseable、新しいtry-with-resource構文で使用するために必要なインターフェイスを実装していません。

これを機能させたい場合は、簡単なラッパーを作成できます。

public class LockWrapper implements AutoCloseable
{
    private final Lock _lock;
    public LockWrapper(Lock l) {
       this._lock = l;
    }

    public void lock() {
        this._lock.lock();
    }

    public void close() {
        this._lock.unlock();
    }
}

これで、次のようなコードを記述できます。

try (LockWrapper someLock = new LockWrapper(new ReentrantLock()))
{
    someLock.lock();
    // ...
}

ただし、古い構文を使用する方がよいと思います。ロックロジックを完全に表示する方が安全です。

于 2011-08-06T08:49:55.807 に答える
21

汎用は、try-with-resourcesステートメントに必要なインターフェースReentrantLockを実装するものも提供するものもありません。ただし、この機能AutoCloseableを提供するので、この概念はJavaAPIにとって完全に異質ではありません。FileChannel.lock()

これまでに示した回答は、ロック呼び出しごとに不要なオブジェクトを作成する、エラーが発生しやすいAPIを公開する、ロックを取得した後、try-finalに入る前に失敗するリスクなど、いくつかの問題がある解決策を共有しています。

Java 7ソリューション:

public interface ResourceLock extends AutoCloseable {

    /**
     * Unlocking doesn't throw any checked exception.
     */
    @Override
    void close();
}

public class CloseableReentrantLock extends ReentrantLock {

    private final ResourceLock unlocker = new ResourceLock() {
        @Override
        public void close() {
            CloseableReentrantLock.this.unlock();
        }
    };

    /**
     * @return an {@link AutoCloseable} once the lock has been acquired.
     */
    public ResourceLock lockAsResource() {
        lock();
        return unlocker;
    }
}

ラムダを使用したリーナーJava8ソリューション:

public class CloseableReentrantLock extends ReentrantLock {

    /**
     * @return an {@link AutoCloseable} once the lock has been acquired.
     */
    public ResourceLock lockAsResource() {
        lock();
        return this::unlock;
    }
}

デモンストレーション:

public static void main(String[] args) {
    CloseableReentrantLock lock = new CloseableReentrantLock();

    try (ResourceLock ignored = lock.lockAsResource()) {
        try (ResourceLock ignored2 = lock.lockAsResource()) {
            System.out.println(lock.getHoldCount());  // 2
        }
    }
    System.out.println(lock.getHoldCount());  // 0
}
于 2017-09-16T00:22:05.230 に答える
7

残されたtry-with-resourceときに作成および破棄されるリソースに対して適切に機能します。try-block存続させる必要のあるリソースでは機能しません。ロックは、使用するたびに作成および破棄されることはありません。それらは生き続け、ただロックされ、ロックが解除されます。これが彼らがそうではない理由AutoClosableです。

他の人がすでに示唆しているように、ラッパーを使用してtry-with-resourceブロックによって作成および破棄し、作成および破棄時にロックおよびロック解除を行うことができます。

于 2014-03-06T07:55:43.147 に答える
3

割り当てコストを無視しない限り、完璧な解決策はありません(ほとんどのアプリケーションプログラマーはできますが、ロックライブラリライターはできません)。次に、ラッパーを使用できます

@RequiredArgsConstructor(access=AccessLevel.PRIVATE)
public final class MgLockCloseable implements AutoCloseable {
    public static MgLockCloseable tryLock(Lock lock) {
        return new MgLockCloseable(lock.tryLock() ? lock : null);
    }

    public static MgLockCloseable lock(Lock lock) {
        lock.lock();
        return new MgLockCloseable(lock);
    }

    @Override public void close() {
        if (isLocked()) {
            lock.unlock();
        }
    }

    public boolean isLocked() {
        return lock != null;
    }

    @Nullable private final Lock lock;
}

この構成では

try (LockCloseable lockCloseable = LockCloseable.lock(lock)) {
    doSomethingUnderLock();
} // automatic release

CRに関する私の質問も参照してください。

于 2016-05-25T13:13:51.663 に答える
2

Runnableロックを使用してtry-with-resourceステートメントを使用するよりも、ロックとaを使用する単純なutilメソッドの方が優れていると思います。

このような:

public static void locked(Lock lock, Runnable r) {
    lock.lock();

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

使用例:

locked(lock, () -> {
    // Do your stuff
});

利点:

  • try-with-resource用に作成されたダミー変数はありません。
  • とてもはっきりしていると思います。

不利益

  • Runnableインスタンスは呼び出しごとに割り当てられますが、他のソリューションのいくつかでは回避されています。しかし、これはほとんどすべての場合に重要ではありません。
  • Java8を使用できる場合にのみ機能します。
于 2019-12-12T20:05:05.650 に答える
1
public class AutoCloseableLockWrapper implements AutoCloseable, Lock{
    private final Lock lock;
    public AutoCloseableLockWrapper(Lock l) {
        this.lock = l;
    }
    @Override
    public void lock() {
        this.lock.lock();
    }

    @Override
    public void lockInterruptibly() throws InterruptedException {
        lock.lockInterruptibly();
    }

    @Override
    public boolean tryLock() {
        return lock.tryLock();
    }

    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return lock.tryLock(time,unit);
    }

    @Override
    public void unlock() {
        lock.unlock();
    }

    @Override
    public Condition newCondition() {
        return lock.newCondition();
    }
    @Override
    public void close() {
        this.lock.unlock();
    }
}
于 2015-08-06T10:28:54.717 に答える
1

user2357112の賢明なアドバイスを考慮に入れて:

public class CloseableLock {

  private class Unlocker implements AutoCloseable {

    @Override
    public void close() throws Exception {
      lock.unlock();
    }

  }

  private final Lock lock;

  private final Unlocker unlocker = new Unlocker();

  public CloseableLock(Lock lock) {
    this.lock = lock;
  }

  public AutoCloseable lock() {
    this.lock.lock();
    return unlocker;
  }

}

使用する:

CloseableLock lock = new CloseableLock(new ReentrantLock());

try (AutoCloseable unlocker = lock.lock()) {
    // lock is acquired, automatically released at the end of this block
} catch (Exception it) {
    // deal with it
}

CloseableLock実装するのは面白いかもしれませんjava.util.concurrent.locks.Lock

于 2017-01-23T14:25:42.653 に答える
1

Stephenの答えとuser2357112のアイデアに基づいて、次のクラスを作成しました。

MyLockクラス自体は、クラスのユーザーにget()を呼び出すように強制するために、それ自体を閉じることはできません。

public class MyLock  {
    public class Session implements AutoCloseable {
        @Override
        public void close() {
            freeLock();
        }
    }

    private ReentrantLock reentrantLock = new ReentrantLock();

    public Session get() { 
        reentrantLock.lock();
        return new Session();
    }

    private void freeLock() {
        reentrantLock.unlock();
    }
}

典型的な使用法は次のとおりです。

MyLock myLock = new MyLock();
try( MyLock.Session session = myLock.get() ) {
    // Lock acquired
}
于 2017-02-22T15:14:25.153 に答える
1

@skoskavのJava8ソリューションをReentrantReadWriteLockに拡張します。

public interface ResourceLock extends AutoCloseable {
    /**
     * Unlocking doesn't throw any checked exception.
     */
    @Override
    void close();
}    
public class CloseableReentrantRWLock extends ReentrantReadWriteLock {

    /**
     * @return an {@link AutoCloseable} once the ReadLock has been acquired
     */
    public ResourceLock lockRead() {
        this.readLock().lock();
        return () -> this.readLock().unlock();
    }

     /**
     * @return an {@link AutoCloseable} once the WriteLock has been acquired.
     */
    public ResourceLock lockWrite() {
        this.writeLock().lock();
        return () -> this.writeLock().unlock();
    }
} 
于 2019-11-17T19:08:07.543 に答える
0

ThreadLocalこれは、ロック要求ごとのルックアップを犠牲にして、うまく機能し、非常に効率的な別のソリューションです。このソリューションは、AutoCloseableパーツ/ラッパーをキャッシュし、スレッドごとに再利用します。

まず、多くのインスタンスを持つResourceLock法線のラッパークラスがあります。Lockこれは私たちが再利用したい部分です。ラッパーはLockインターフェイスを実装するため、通常のように動作しますLockが、自動で閉じることができます。

public class ResourceLock implements AutoCloseable, Lock {

    private Lock lock;

    public ResourceLock(Lock lock) {
        this(lock, true);
    }
    
    public ResourceLock(Lock lock, boolean eagerLock) {
        this.lock = lock;
        
        if (eagerLock) {
            lock.lock();
        }
    }

    public void lock() {
        lock.lock();
    }

    public void lockInterruptibly() throws InterruptedException {
        lock.lockInterruptibly();
    }

    public Condition newCondition() {
        return lock.newCondition();
    }

    ResourceLock setLock(Lock lock) {
        this.lock = lock;

        return this;
    }

    public boolean tryLock() {
        return lock.tryLock();
    }

    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return lock.tryLock(time, unit);
    }

    public void unlock() {
        lock.unlock();
    }

    @Override
    public void close() {
        lock.unlock();
    }
}

再利用可能な形式では、次のように使用するだけです。

try (ResourceLock ignore = new ResourceLock(rwl.writeLock())) {
    // Resource locked in here
}

ResourceLockまたは、スレッドごとにオブジェクトを再利用できるキャッシュ対応ラッパーを追加することもできます。

public class ResourceLockCache {

    private final Lock lock;
    private final Supplier<ResourceLock> cachingStrategy;

    public ResourceLockCache(Lock lock) {
        this.lock = lock;

        final ThreadLocal<ResourceLock> strategy = new ThreadLocal<ResourceLock>() {

            @Override
            protected ResourceLock initialValue() {
                return new ResourceLock();
            }

        };

        this.cachingStrategy = strategy::get;
    }

    public ResourceLockCache(Lock lock, Supplier<ResourceLock> cachingStrategy) {
        this.lock = lock;
        this.cachingStrategy = cachingStrategy;
    }

    public ResourceLock getAsResource() {
        final ResourceLock activeLock = cachingStrategy.get();

        activeLock.setLock(lock);

        return activeLock;
    }

    public ResourceLock getAsResourceAndLock() {
        final ResourceLock activeLock = cachingStrategy.get();

        activeLock.setLock(lock);
        activeLock.lock();

        return activeLock;
    }
}

これで、自動で閉じることができる各ロックを再利用できます。

ResourceLockCache rlc = new ResourceLockCache(new ReentrantLock());
// Or this to change caching strategy to new object per lock
ResourceLockCache rlc2 = new ResourceLockCache(new ReentrantLock(), ResourceLock::new);

try (ResourceLock ignore = rlc.getAsResourceAndLock()) {
    // Resource locked in here
}

またReadWriteLock、より複雑なロックのニーズに対応するバリアントもあります。インターフェイスを実装しているため、次のReadWriteLockような複雑なロック戦略を使用できるため、より用途が広くなりますtryLock

public class ResourceRWLockCache implements ReadWriteLock {

    private final ReadWriteLock rwl;
    private final Supplier<ResourceLock> cachingStrategy;

    public ResourceRWLockCache(ReadWriteLock rwl) {
        this.rwl = rwl;

        final ThreadLocal<ResourceLock> strategy = new ThreadLocal<ResourceLock>() {

            @Override
            protected ResourceLock initialValue() {
                return new ResourceLock();
            }

        };

        this.cachingStrategy = strategy::get;
    }

    public ResourceRWLockCache(ReadWriteLock rwl, Supplier<ResourceLock> cachingStrategy) {
        this.rwl = rwl;
        this.cachingStrategy = cachingStrategy;
    }

    public ResourceLock readLock() {
        final ResourceLock activeLock = cachingStrategy.get();

        activeLock.setLock(rwl.readLock());

        return activeLock;
    }

    public ResourceLock readLockAndLock() {
        final ResourceLock activeLock = cachingStrategy.get();

        activeLock.setLock(rwl.readLock());
        activeLock.lock();

        return activeLock;
    }

    public ResourceLock writeLock() {
        final ResourceLock activeLock = cachingStrategy.get();

        activeLock.setLock(rwl.writeLock());

        return activeLock;
    }

    public ResourceLock writeLockAndLock() {
        final ResourceLock activeLock = cachingStrategy.get();

        activeLock.setLock(rwl.writeLock());
        activeLock.lock();

        return activeLock;
    }
}
ResourceRWLockCache rwl = new ResourceRWLockCache(new ReentrantReadWriteLock());
// Or this to change caching strategy to new object per lock
ResourceRWLockCache rwl2 = new ResourceRWLockCache(new ReentrantReadWriteLock(), ResourceLock::new);

try (ResourceLock ignore = rwl.writeLockAndLock()) {
    // Resource locked in here
}

このソリューションが、リソースリリースハンドラーを再利用するシングルおよびマルチロック戦略に役立つことを願っています。

于 2020-12-17T18:31:20.467 に答える
0

skoskavの優れた答えをReadWriteLock次のように拡張します。

CloseableLock.java:

public interface CloseableLock extends AutoCloseable
{
    /**
     * Release the lock.
     */
    @Override
    void close();
}

ReadWriteLockAsResource:

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;

/**
 * Enables the use of {@code try-with-resources} with {@code ReadWriteLock}.
 */
public final class ReadWriteLockAsResource
{
    private final ReadWriteLock lock;

    /**
     * @param lock a lock
     * @throws NullPointerException if {@code lock} is null
     */
    public ReadWriteLockAsResource(ReadWriteLock lock)
    {
        if (lock == null)
          throw new NullPointerException("lock may not be null");
        this.lock = lock;
    }

    /**
     * Starts a new read-lock.
     *
     * @return the read-lock as a resource
     */
    public CloseableLock readLock()
    {
        Lock readLock = lock.readLock();
        readLock.lock();
        return readLock::unlock;
    }

    /**
     * Starts a new write-lock.
     *
     * @return the write-lock as a resource
     */
    public CloseableLock writeLock()
    {
        Lock writeLock = lock.writeLock();
        writeLock.lock();
        return writeLock::unlock;
    }

    /**
     * Returns a new condition.
     *
     * @return a new condition
     */
    public Condition newCondition()
    {
        return lock.writeLock().newCondition();
    }
}

使用法:

public final class GuideToTheUniverse
{
    private final LockAsResource lock = new LockAsResource(new ReentrantReadWriteLock());
    
    public int answerToLife()
    {
        try (CloseableLock writeLock = lock.writeLock())
        {
            System.out.println("Look ma', no hands!");
            return 42;
        }
    }
}
于 2022-02-19T22:52:38.143 に答える