锁是否具有自动关闭功能?

53

Locks是否是自动可关闭的?也就是说,是否可以这样做:

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

我能这样说吗:

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

...在Java 7中?


3
您可以提出请求,让他们这样做。 - ratchet freak
12个回答

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

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
}

希望这个解决方案能够对于单锁和多锁策略以及重复使用资源释放处理程序有所帮助。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接