Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/390.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在Java中释放信号量对象的正确方法是什么?_Java_Multithreading_Concurrency_Semaphore - Fatal编程技术网

在Java中释放信号量对象的正确方法是什么?

在Java中释放信号量对象的正确方法是什么?,java,multithreading,concurrency,semaphore,Java,Multithreading,Concurrency,Semaphore,这是大多数在线资源中编写的代码。但这是不正确的,因为考虑线程阻塞时的场景,然后中断。 即使线程尚未获得锁,它仍将释放锁。这是不正确的。那么,在java中释放信号量的正确实现是什么呢 Semaphore lock = new Semaphore(1); add(){ try { lock.acquire(); // critical section } catch (InterruptedException e) {

这是大多数在线资源中编写的代码。但这是不正确的,因为考虑线程阻塞时的场景,然后中断。 即使线程尚未获得锁,它仍将释放锁。这是不正确的。那么,在java中释放信号量的正确实现是什么呢

Semaphore lock = new Semaphore(1);

  add(){
    try {
        lock.acquire();

        // critical section

    } catch (InterruptedException e) {

          Thread.currentThread().interrupt();

    }finally{
       lock.release()
    }
}
我认为这是正确的解决方案:-。是吗

try {
    lock.acquire();

    try {
        // do some stuff here
    } finally {

        lock.release();

    }
} catch(InterruptedException ie) {

    Thread.currentThread().interrupt();
    throw new RuntimeException(ie);

}

信号量没有所有权的概念。许可证不是真的,它只是信号灯保持的计数。所以问题是,在方法执行之后,计数是否正确? 如果你被打断了,你会留下一个或两个可用的许可证吗

api文档中Oracle站点上的信号量示例没有任何finally块,在很多情况下它们并不相关

如果您使用此信号量来实现互斥,并且它只有一个许可证,我希望它应该有一个try finally块,就像使用锁一样(来自):

如果代码接受InterruptedException并让线程继续运行,那么方法末尾的计数是否正确就变得很重要,它可能会阻止其他调用获取许可

每当我得到某样东西,使用它,然后释放它时,我使用的一般模式是将它置于try块之上,然后在try块中使用它,在finally块中关闭/释放/清理。这适用于IO、JDBC资源等。您可以尝试避免这种情况,将采集放在try块中,然后 在清理之前检查空值。您还可以尝试在finally块中执行太多操作,在close时无法捕获异常,并造成资源泄漏。最好尽量减少finally块中的代码量

一个正确的例子是(摘自Java并发性一书) 在实践中):

这显示了一个带有finally的try块,该块执行一些清理(如果结果没有向集合中添加任何内容,则释放许可证),其中在输入try块之前调用acquire

在本例中,如果acquire调用被移动到try块中,则无所谓。我认为将调用置于try块之上是更好的样式,但在本例中,它不会影响正确性,因为它使用标志来决定是否发布许可证

我将使用一个类似于jcip示例中的标志,在获取后将其设置为true,并且仅在设置了标志时才释放。这样您就可以将acquire放在try块中

    boolean wasAcquired = false;
    try {      
         sem.acquire();
        wasAcquired = true;
        // crit sect
     } catch (InterruptedException e) {
        Thread.currentThread.interrupt();
    } finally {
        if (wasAcquired)
            sem.release();
    }

或考虑获取不中断()。但是想想看,如果这些调用没有抛出InterruptedException,那么在收到中断请求时,代码的哪一部分确保代码实际停止工作?看起来您可能会陷入一个无效的循环,线程尝试获取、抛出InterruptedException、捕获它并设置中断状态,然后在下一次线程尝试获取时重复执行相同的操作。抛出InterruptedException可以让您快速响应取消请求,同时确保在finally块中完成清理

即使线程尚未获得锁,它仍将释放锁。这是不正确的

不,不是。这里没有什么不合适的。从:

不要求释放许可证的线程必须通过调用
acquire()获得该许可证。
信号量的正确使用由应用程序中的编程约定确定


这里没有需要解决的问题。

只需添加一个标志,指示是否已获取锁:

boolean acquired = false;
try {
    lock.acquire();
    acquired = true;
    // critical section

} catch (InterruptedException e) {

     // do anything

} finally {
   if (acquired) {
       lock.release()
   }
}

另一种解决方案是使用
信号量。AcquireUnterruptibly()

acquire()抛出异常,因此必须进行尝试。您可以发布正确的实现吗?这并不意味着它必须在同一个try块中。而且InterruptedException可能甚至不应该被处理,但应该被处理。我想在代码本身中处理异常。@Karen:您可以使用AcquireUnterruptibly()而不必弄乱它。非常感谢您的解释。
release()的调用
信号量的Javadoc允许
并不意味着问题中的代码是正确的。如果
acquire()
被中断,它仍然会意外地增加许可证的数量,这在大多数情况下可能是一个错误。EJP:假设你是我的投票人,请看看我更新的答案是否不那么令人反感。@PhilippWendler the Javadoc恰恰允许此代码,这意味着需要信号量实现在其中正确运行。您可以发布这方面的其余代码吗?因此,我们可以清楚地知道你想要实现什么。谢谢你,现在更清楚了,不要破坏你自己的帖子。你可以删除它们。
    boolean wasAcquired = false;
    try {      
         sem.acquire();
        wasAcquired = true;
        // crit sect
     } catch (InterruptedException e) {
        Thread.currentThread.interrupt();
    } finally {
        if (wasAcquired)
            sem.release();
    }
boolean acquired = false;
try {
    lock.acquire();
    acquired = true;
    // critical section

} catch (InterruptedException e) {

     // do anything

} finally {
   if (acquired) {
       lock.release()
   }
}