[英]Are Locks AutoCloseable?
Are Locks
auto-closeable? Locks
可以自动关闭吗? That is, instead of:也就是说,而不是:
Lock someLock = new ReentrantLock();
someLock.lock();
try
{
// ...
}
finally
{
someLock.unlock();
}
...can I say: ...我能说......么:
try (Lock someLock = new ReentrantLock())
{
someLock.lock();
// ...
}
...in Java 7? ...在 Java 7?
I was looking into doing this myself and did something like this:我正在考虑自己做这件事并做了这样的事情:
public class CloseableReentrantLock extends ReentrantLock implements AutoCloseable {
public CloseableReentrantLock open() {
this.lock();
return this;
}
@Override
public void close() {
this.unlock();
}
}
and then this as usage for the class:然后这是该课程的用法:
public class MyClass {
private final CloseableReentrantLock lock = new CloseableReentrantLock();
public void myMethod() {
try(CloseableReentrantLock closeableLock = lock.open()) {
// locked stuff
}
}
}
No, neither the Lock
interface (nor the ReentrantLock
class) implement the AutoCloseable
interface, which is required for use with the new try-with-resource syntax.不,
Lock
接口(或ReentrantLock
类)都没有实现AutoCloseable
接口,这是与新的 try-with-resource 语法一起使用所必需的。
If you wanted to get this to work, you could write a simple wrapper:如果你想让它工作,你可以写一个简单的包装器:
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();
}
}
Now you can write code like this:现在你可以写这样的代码:
try (LockWrapper someLock = new LockWrapper(new ReentrantLock()))
{
someLock.lock();
// ...
}
I think you're better off sticking with the old syntax, though.不过,我认为您最好坚持使用旧语法。 It's safer to have your locking logic fully visible.
让锁定逻辑完全可见更安全。
The general-purpose ReentrantLock
neither implements nor provides anything that implements the AutoCloseable
interface necessary for a try-with-resources statement.通用
ReentrantLock
既不实现也不提供任何实现 try-with-resources 语句所需的AutoCloseable
接口的东西。 The concept isn't completely foreign to the Java API though, as FileChannel.lock()
offers this functionality.不过这个概念对于 Java API 来说并不是完全陌生的,因为
FileChannel.lock()
提供了这个功能。
The answers given so far share solutions that have some issues, such as creating an unnecessary object on each lock call, exposing an error-prone API or risk failing after the lock is acquired but before the try-finally is entered.到目前为止给出的答案共享存在一些问题的解决方案,例如在每次锁定调用时创建一个不必要的对象、暴露容易出错的 API 或在获取锁定之后但在进入 try-finally 之前失败的风险。
Java 7 solution: 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;
}
}
Leaner Java 8 solution using a lambda:使用 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;
}
}
Demonstration:示范:
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
}
The try-with-resource
works well for resources which are created and destroyed when try-block
is left. try-with-resource
适用于在try-block
离开时创建和销毁的资源。 It does not work for resources which need to be kept alive.它不适用于需要保持活动状态的资源。 Locks are not created and destroyed upon each usage.
不会在每次使用时创建和销毁锁。 They are kept alive and just locked and unlocked.
他们保持活力,只是锁定和解锁。 This is why they are not
AutoClosable
.这就是它们不是
AutoClosable
。
As others already suggested a wrapper can be used to be created and destroyed by the try-with-resource
block and to do the locking and unlocking upon creation and destruction.正如其他人已经建议的那样,可以使用
try-with-resource
块创建和销毁包装器,并在创建和销毁时进行锁定和解锁。
There's no perfect solution, unless you ignore the allocation costs (most application programmers can, but the lock library writers can not).没有完美的解决方案,除非您忽略分配成本(大多数应用程序程序员可以,但锁库编写者不能)。 Then you can use a wrapper
然后你可以使用包装器
@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;
}
in this construct在这个构造中
try (LockCloseable lockCloseable = LockCloseable.lock(lock)) {
doSomethingUnderLock();
} // automatic release
See also my question on CR .另请参阅我关于 CR 的问题。
I think a simple util method which takes a lock and a Runnable
is better than using the try-with-resource statement with locks.我认为使用锁和
Runnable
的简单 util 方法比使用带锁的 try-with-resource 语句更好。
Like this:像这样:
public static void locked(Lock lock, Runnable r) {
lock.lock();
try {
r.run();
} finally {
lock.unlock();
}
}
Usage example:用法示例:
locked(lock, () -> {
// Do your stuff
});
Advantages:优点:
Disadvantage坏处
Runnable
instance is allocated for each calls, something that some of the other solutions avoid.Runnable
实例,这是其他一些解决方案避免的。 But this is insignificant in almost all cases.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();
}
}
Taking user2357112's shrewd advice into account:考虑到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;
}
}
Use:采用:
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
}
Could be interesting to make CloseableLock
implement java.util.concurrent.locks.Lock
.让
CloseableLock
实现java.util.concurrent.locks.Lock
可能很有趣。
Building on Stephen's answer and user2357112's idea, I have written the following class.基于斯蒂芬的回答和 user2357112 的想法,我编写了以下课程。
The MyLock class itself is not closeable itself, to force users of the class to call get(). 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();
}
}
Here is a typical use:这是一个典型的用途:
MyLock myLock = new MyLock();
try( MyLock.Session session = myLock.get() ) {
// Lock acquired
}
Extending the Java8 solution of @skoskav to ReentrantReadWriteLock:将@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();
}
}
Here is another solution that works great and is super efficient at the expense of a ThreadLocal
lookup per lock request.这是另一种效果很好的解决方案,它以每个锁请求的
ThreadLocal
查找为代价,非常高效。 This solution caches the AutoCloseable
part/wrapper and reuses it on a per-thread basis.此解决方案缓存
AutoCloseable
部件/包装器并在每个线程的基础上重用它。
First we have a wrapper class ResourceLock
around a normal Lock
that we will have many instances of.首先,我们有一个包装类
ResourceLock
围绕着一个普通的Lock
,我们将拥有许多实例。 This is the part we want to reuse.这是我们想要重用的部分。 The wrapper implements the
Lock
interface so it behaves like a normal Lock
but one that can be auto closed:包装器实现了
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();
}
}
In none reusable form you would simply use it like this:在不可重复使用的形式中,您只需像这样使用它:
try (ResourceLock ignore = new ResourceLock(rwl.writeLock())) {
// Resource locked in here
}
Or we can add a caching capable wrapper which will let us reuse ResourceLock
objects per thread.或者我们可以添加一个具有缓存能力的包装器,这将使我们能够重用每个线程的
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;
}
}
Now we can use reuse each auto-closeable lock:现在我们可以使用重用每个自动关闭的锁:
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
}
Also have a ReadWriteLock
variant for more complex locking needs.还有一个
ReadWriteLock
变体,用于更复杂的锁定需求。 It implements the ReadWriteLock
interface so its more versatile as you can use complex locking strategies such as tryLock
etc:它实现了
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
}
Hope this solution helps for single and multi lock strategies with re-using the resource release handlers.希望这个解决方案有助于单锁和多锁策略,重用资源释放处理程序。
Extending skoskav 's excellent answer to ReadWriteLock
:扩展skoskav对
ReadWriteLock
的出色回答:
CloseableLock.java: CloseableLock.java:
public interface CloseableLock extends AutoCloseable
{
/**
* Release the lock.
*/
@Override
void close();
}
ReadWriteLockAsResource: 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();
}
}
Usage:用法:
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.