Locks
是否是自动可关闭的?也就是说,是否可以这样做:
Lock someLock = new ReentrantLock();
someLock.lock();
try
{
// ...
}
finally
{
someLock.unlock();
}
我能这样说吗:
try (Lock someLock = new ReentrantLock())
{
someLock.lock();
// ...
}
...在Java 7中?
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()
而不是 open()
呢? - Thomas Ahlelock()
是继承自ReentrantLock
并且具有void
返回类型。由于在不破坏继承关系的情况下无法使用lock()
,因此选择open()
来配合close()
更加合理。 - Andrewopen
return a separate object whose close
releases the lock. Otherwise, you risk people doing try(CloseableReentrantLock closeableLock = lock)
without the open
call."我会考虑使锁本身不是AutoCloseable,并改为让open
返回一个单独的对象,该对象的close
方法释放锁。否则,你将面临人们在没有进行open
调用的情况下,使用try(CloseableReentrantLock closeableLock = lock)
的风险。 - user2357112通用的ReentrantLock
既没有实现也没有提供实现AutoCloseable
接口所需的内容,该接口在使用try-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
}
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();
// ...
}
我认为你最好坚持使用旧的语法。这样可以更安全地让你的锁定逻辑完全可见。
lock()
语句要放在try块之外。 - Adrian Shumtry-with-resource
可以很好地工作。但它对需要保持活动的资源无法起作用。锁不是在每次使用时创建和销毁的。它们被保持活动,只是被锁定和解除锁定。这就是为什么它们不是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
比使用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 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
可能会很有趣。
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();
}
}
close()
函数中释放锁,这会给用户带来更多的工作,他们必须始终使用新的 AutoCloseableLockWrapper
创建 try-with-resources 块,然后在任何可能失败或返回之前进行 lock
。同样,tryLock
函数也很“复杂”,因为你可能无法获取锁,但在退出 try-with-resources 块时它将被解锁... - David Rodríguez - dribeas在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
}
将@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();
}
}