4

在 try-catch 块中包装信号量操作的正确方法是什么?如果获取操作在获取了一些(但不是全部)请求的许可后被中断,会发生什么?你怎么知道又要释放多少?释放是否应该进入“最终”块,但是如果动作被中断,您是否可能释放您没有获得的许可?

try {
    lock.acquire(permits);

    //Do some things that require synchronization

    //Make sure to release all of the permits again!
    lock.release(permits);
} catch (InterruptedException e) {
    log.error("Interrupted!");
}
4

3 回答 3

4

Semaphore.acquire(int)方法是全有或全无操作,要么获得所有请求的许可,要么阻止。您可以在代码周围使用双重尝试,或者让(可能的)中断异常从获取调用堆栈中冒泡。

双重尝试块:

try {
    lock.acquire(permits);

    try {
        // do some stuff here
    } finally {
        lock.release(permits);
    }
} catch(final InterruptedException ie) {
    // handle acquire failure here
}

气泡“收购”异常:

lock.acquire(permits);

try {
    // do some stuff here
} finally {
    lock.release(permits);
}

在切线中,请记住信号量必须通过严格的编程约定保持平衡,因此您应该始终释放尽可能多的许可。

于 2013-02-28T15:35:25.733 回答
0

add方法BoundedHashSet为例,

    public boolean add(T o) throws InterruptedException {
        sem.acquire();
        boolean wasAdded = false;
        try {
            wasAdded = set.add(o);
            return wasAdded;
        } finally {
            if (!wasAdded)
                sem.release();
        }
    }

如果sem.acquire();抛出 InterruptedException,则跳过 try 块和 finally 块。

否则,我们成功获取信号量,try 块和 finally 块将被一起执行。也就是说,我们将释放我们获得的相同数量的许可证。

于 2019-12-11T14:52:19.123 回答
0

要添加@Perception 的答案,您可以选择分离嵌套的双try-catch 并在acquire 块上添加重试,这适合我的用例。

int retry_limit = 10;
boolean acquired = lock.tryAcquire(permits);
for (int i=0; i < retry_limit && !acquired; ++i) {
    try {
        Thread.sleep(1000);
    }
    acquired = lock.tryAcquire(permits);
}
if (!acquired) {
    throw new IllegalStateException("Unable to acquire permits");
}
try {
    // do some stuff here
} catch(InterruptedException ie) {
    // handle acquire failure here
} catch(Exception ex) {
    // handle other exceptions here
} finally {
    lock.release(permits);
}
于 2021-06-28T21:21:23.073 回答