这是另一种效果很好且超级高效的解决方案,但代价是每个锁定请求都需要查找 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
}
希望此解决方案有助于通过重用资源释放处理程序来实现单锁和多锁策略。