![](/img/trans.png)
[英]Any risk in a AutoCloseable wrapper for java.util.concurrent.locks.Lock?
[英]Are Locks AutoCloseable?
Locks
可以自动关闭吗? 也就是说,而不是:
Lock someLock = new ReentrantLock();
someLock.lock();
try
{
// ...
}
finally
{
someLock.unlock();
}
...我能说......么:
try (Lock someLock = new ReentrantLock())
{
someLock.lock();
// ...
}
...在 Java 7?
我正在考虑自己做这件事并做了这样的事情:
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
}
}
}
不, 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();
// ...
}
不过,我认为您最好坚持使用旧语法。 让锁定逻辑完全可见更安全。
通用ReentrantLock
既不实现也不提供任何实现 try-with-resources 语句所需的AutoCloseable
接口的东西。 不过这个概念对于 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
}
try-with-resource
适用于在try-block
离开时创建和销毁的资源。 它不适用于需要保持活动状态的资源。 不会在每次使用时创建和销毁锁。 他们保持活力,只是锁定和解锁。 这就是它们不是AutoClosable
。
正如其他人已经建议的那样,可以使用try-with-resource
块创建和销毁包装器,并在创建和销毁时进行锁定和解锁。
没有完美的解决方案,除非您忽略分配成本(大多数应用程序程序员可以,但锁库编写者不能)。 然后你可以使用包装器
@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 的问题。
我认为使用锁和Runnable
的简单 util 方法比使用带锁的 try-with-resource 语句更好。
像这样:
public static void locked(Lock lock, Runnable r) {
lock.lock();
try {
r.run();
} finally {
lock.unlock();
}
}
用法示例:
locked(lock, () -> {
// Do your stuff
});
优点:
坏处
Runnable
实例,这是其他一些解决方案避免的。 但这在几乎所有情况下都是微不足道的。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();
}
}
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
可能很有趣。
基于斯蒂芬的回答和 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
}
将@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();
}
}
这是另一种效果很好的解决方案,它以每个锁请求的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
}
希望这个解决方案有助于单锁和多锁策略,重用资源释放处理程序。
扩展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;
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.