此互斥阻止对目标资源的并发访问。一个关键的要求是在(调度程序)线程中获得并在另一个(worker)线程中释放的能力。不幸的是,这意味着不能使用广泛使用的原语,如隐式锁(synchronized)或来自java.util.concurrent的Lock,因为它们不允许获取锁的线程将其传递给其他线程。
当互斥锁被保存在一个长期存在的变量(甚至一个实例字段)并且方法.release()不止一次被调用时,这一关键需求可能会导致编程错误。我们希望这样的编程错误能够抛出特定的运行时异常,而不是默默地允许不正确的程序执行。
表现并不重要。此锁通常不会被两个或多个线程争用。正确性是必需的,可读性很高。
API取自Semaphore (这个类可以看作是一个Semaphore,只有1许可,不允许许可数量变为负数):
acquire()acquire(long timeout, TimeUnit timeUnit)tryAcquire()tryAcquire(long timeout, TimeUnit timeUnit)release(Combination combination)客户端代码中的使用模式:
Combination combination = lock.acquire();
threadPool.submit(() -> {
try { ... } finally {
lock.release(combination);
}
});请注意,每次获得锁时,都会生成新的Combination并将其返回给调用方。只有使用完全相同的release()调用Combination才能使锁再次可用。一旦释放,内部组合将被清除(设置为null),并且锁被解锁。
public class CombinationLock {
// This lock is used to synchronize access to the combination field
private final Lock lock = new ReentrantLock();
private final Condition lockReleased = lock.newCondition();
// Initially null, this field is set to a non-null value from acquire/tryAcquire methods
// and then set to null again from release().
private Combination combination;
/**
* Block the caller thread indefinitely until the lock is acquired
*
* @return the combination for releasing the lock
* @throws InterruptedException if waiting thread is interrupted
*/
public Combination acquire() throws InterruptedException {
lock.lockInterruptibly();
try {
while (combination != null) {
lockReleased.await();
}
combination = Combination.generate();
return combination;
} finally {
lock.unlock();
}
}
/**
* Wait for the lock to be available. If available at the time of invocation, lock and return the
* combination to release it later, otherwise just return null.
*
* @return null, or the combination for releasing the lock
*/
public Combination tryAcquire() throws InterruptedException {
boolean locked = lock.tryLock();
if (locked) {
try {
if (combination == null) {
combination = Combination.generate();
return combination;
} else {
return null;
}
} finally {
lock.unlock();
}
} else {
return null;
}
}
/**
* Wait for the given timeout, otherwise return null
*
* @param timeout in arbitrary unit
* @param unit the timeout unit
* @return NULL, of the combination to release the lock
* @throws InterruptedException if thread is interrupted
*/
public Combination tryAcquire(long timeout, TimeUnit unit) throws InterruptedException {
Stopwatch stopwatch = Stopwatch.createStarted();
boolean locked = lock.tryLock(timeout, unit);
if (locked) {
try {
while (combination != null) {
long remainingWaitTimeNanos = unit.toNanos(timeout) - stopwatch.elapsed(TimeUnit.NANOSECONDS);
boolean timeRunOut = lockReleased.await(remainingWaitTimeNanos, TimeUnit.NANOSECONDS);
if (timeRunOut) {
return null;
}
}
combination = Combination.generate();
return combination;
} finally {
lock.unlock();
}
} else {
return null;
}
}
public boolean isLocked() {
lock.lock();
try {
return combination != null;
} finally {
lock.unlock();
}
}
/**
* Release the lock
*
* @param combination previously generated from acquire/tryAcquire
*/
public void release(Combination combination) {
Preconditions.checkNotNull(combination, "Given combination must be non-null. Did you forget to check [combination != null] after calling tryAcquire()?");
lock.lock();
try {
if (!isLocked()) {
throw new RuntimeException("This lock is not locked");
}
if (this.combination.equals(combination)) {
this.combination = null;
lockReleased.signalAll();
} else {
throw new RuntimeException("Someone else acquired the lock since you got your combination so you must have released it");
}
} finally {
lock.unlock();
}
}
}发布于 2022-11-15 21:49:39
欢迎,并发很难!你测试过这个代码了吗?你有单元测试吗?
isLocked方法似乎毫无用处。它有什么意义?它是锁的?如果您在这么短的时间内获得锁(仅仅是为了获取boolean,则方法中的信息是无用的,因为它可能很快就过时了。如果你想对它作出反应,你想用原子的方式来做。另一种方法是从它中移除锁,因为在这种情况下,它并没有真正对您有任何帮助。nulls (即使您的示例用法也会在acquire失败时抛出NullPointerException!)在acquire方法中,考虑使用Optional或抛出异常。lock总是被锁定或解锁,较少容易出错的方法有一些基本的方法框架,这是为您做的,然后在“中间”执行您的功能块。然后,您的原子关键部分是这些lambda,它们与同步代码分离,同步代码位于一个地方。代码更简洁,更容易出错。Combination类的存在没有任何意义。是为了确保不发布不正确的CombinationLock实例吗?https://codereview.stackexchange.com/questions/281234
复制相似问题