【问题标题】:How can I safely use semaphores in a while loop?如何在 while 循环中安全地使用信号量?
【发布时间】:2014-05-15 22:19:19
【问题描述】:

在以下代码片段中,我使用信号量来同步对某些资源的访问。

public void m () {
    permit.acquire ();
    while (!canFoo ()) {
        permit.release ();
        reticulateSpines ();
        permit.acquire ();
    }
    doFoo ();
    permit.release ();
}

将获取/释放周期包含在 try/finally 中可能是合理的。考虑到 while 循环的存在,我该怎么做?

【问题讨论】:

    标签: java synchronization semaphore try-finally


    【解决方案1】:

    根据每个acquire必须发布的原则,我建议:

    private final Semaphore permit = new Semaphore(8, true);
    private final Random random = new Random();
    
    private boolean canFoo() {
        return random.nextBoolean();
    }
    
    private void doFoo() {
        System.out.println("Foo done!");
    }
    
    private void reticulateSpines() {
        System.out.println("Spines reticulated!");
    }
    
    public void m() throws InterruptedException {
        permit.acquire();
        try {
            while (!canFoo()) {
                permit.release();
                try {
                    reticulateSpines ();
                } finally {
                    permit.acquire();
                }
            }
            doFoo();
        } finally {
            permit.release();
        }
    }
    

    但是 - 我不确定您是否按预期使用信号量。它看起来更像是您正在寻找类似 @​​987654323@ 的东西,它可以消除自旋锁循环。

    ReadWriteLock fooLock = new ReentrantReadWriteLock();
    Lock fooReadLock = fooLock.readLock();
    Lock fooWriteLock = fooLock.writeLock();
    
    public void n() throws InterruptedException {
        fooWriteLock.lock();
        try {
            doFoo();
        } finally {
            fooWriteLock.unlock();
        }
    }
    

    甚至可能

    public void o() throws InterruptedException {
        while (!fooWriteLock.tryLock()) {
            reticulateSpines();
        }
        try {
            doFoo();
        } finally {
            fooWriteLock.unlock();
        }
    }
    

    【讨论】:

    • (我在中心添加了一个方法调用(不需要同步)。)
    • 我认为这种结构是个好主意,但我觉得try .. finally { acquire } 很奇怪。我不确定,但这是处理中心可能出现异常情况的正确方法吗?
    • @mafu - 同样的原则适用 - 如果您获得了信号量,您必须释放它,但一旦获得它并选择释放它,您必须 再次获取它,否则您最终可能会在外部最终释放未获取的信号量。此代码将始终使信号量保持与找到它时相同的状态,即使被中断。
    • 如果内部获取阻塞,然后被Thread.interrupt() 中断,外部最终在没有事先获取许可的情况下运行。
    • 啊,您可以通过添加一个布尔标志来缓解这种情况,该标志在内部释放之前被清除并在内部获取之后立即激活,并且外部最终仅在设置该平面时才释放。它看起来很臃肿,但它应该是“正确的”。
    猜你喜欢
    • 2017-02-17
    • 1970-01-01
    • 2020-02-20
    • 1970-01-01
    • 2021-01-13
    • 2015-09-26
    • 2015-05-02
    • 1970-01-01
    • 2016-01-12
    相关资源
    最近更新 更多