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

通用的ReentrantLock既不实现也不提供实现AutoCloseabletry-with-resources 语句所需接口的任何东西。不过,这个概念对于 Java API 来说并不完全陌生,因为FileChannel.lock()它提供了这个功能。

到目前为止给出的答案共享存在一些问题的解决方案,例如在每次锁定调用时创建不必要的对象、暴露容易出错的 API 或在获取锁定之后但在进入 try-finally 之前存在失败的风险。

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;
    }
}

使用 lambda 的更精简的Java 8解决方案:

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

我认为一个简单的 util 方法需要一个锁和 aRunnable比使用带有锁的 try-with-resource 语句更好。

像这样:

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实例,这是其他一些解决方案避免的。但这在几乎所有情况下都是微不足道的。
  • 仅当您可以使用 Java 8 时才有效。
于 2019-12-12T20:05:05.650 回答
1

基于斯蒂芬的回答和 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
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

Extending the Java8 solution of @skoskav to 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 回答
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 回答
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 回答
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 回答