为了账号安全,请及时绑定邮箱和手机立即绑定

为什么信号量工作而 ReentrantLock 不工作?

为什么信号量工作而 ReentrantLock 不工作?

炎炎设计 2023-03-23 16:24:19
我正在尝试 Leetcode 中的一个简单的并发问题。我在大学里非常简要地研究过这个主题,但没有使用 Java API。似乎我不能在ReentrantLock不Lock遇到IllegalMonitorStateException. 然而 a Semaphore(这似乎有点矫枉过正,因为我只需要使用二进制值)似乎工作正常。为什么是这样?二进制信号量与 ReentrantLock建议(如果我理解正确的话)二进制锁只能由获取它的线程释放,这可能是我的问题的根源,因为我在下面的代码中的构造函数中获取它们。有没有其他自然的方法可以使用锁/不使用信号量来解决这个问题?Lock带有引发 s 的代码IllegalMonitorStateException:import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;class Foo {    private Lock printedFirst;    private Lock printedSecond;    public Foo() {        this.printedFirst = new ReentrantLock();        this.printedSecond = new ReentrantLock();        printedFirst.lock();        printedSecond.lock();    }    public void firsSemaphore带有按预期工作的 s 的代码:import java.util.concurrent.Semaphore;class Foo {    private Semaphore printedFirst;    private Semaphore printedSecond;    public Foo() {        this.printedFirst = new Semaphore(0);        this.printedSecond = new Semaphore(0);    }    public void first(Runnable printFirst) throws InterruptedException {        printFirst.run();        printedFirst.release();    }    public void second(Runnable printSecond) throws InterruptedException {        printedFirst.acquire();        try {            printSecond.run();        } finally {            printedSecond.release();        }    }    public void third(Runnable printThird) throws InterruptedException {        printedSecond.acquire();        try {            printThird.run();        } finally {}    }}t(Runnable printFirst) throws InterruptedException {        printFirst.run();        printedFirst.unlock();    }    public void second(Runnable printSecond) throws InterruptedException {        printedFirst.lock();        try {            printSecond.run();        } finally {            printedSecond.unlock();        }    }    public void third(Runnable printThird) throws InterruptedException {        printedSecond.lock();        try {            printThird.run();        } finally {}    }}
查看完整描述

3 回答

?
蝴蝶不菲

TA贡献1810条经验 获得超4个赞

同步有两个主要用例:资源访问和事件传递。您试图解决的并发问题需要事件传递:第二个线程等待第一个线程的信号,第三个线程等待第二个线程的信号。信号,我的意思是,是空事件,它们带有事件已经发生的事实,没有任何额外的细节。

Semaphores 非常适合信号传递(尽管也可用于资源访问)。ReentrantLocks 是为资源访问而设计的。从技术上讲,任何事件传递机制都是建立在资源访问之上的。因此您可以使用ReentrantLocks 进行信号传递,但它需要一些额外的编程(在@billie 的回答中演示)。


查看完整回答
反对 回复 2023-03-23
?
森林海

TA贡献2011条经验 获得超2个赞

正如您所发现的,信号量易于实现和推理 - 获取锁定、执行操作、释放锁定。ReentrantLocks 是不同的,旨在服务于您发布的示例中不存在的更复杂的目的。是的,ReentrantLocks 由单个线程拥有,只能由该线程释放。

如果您的目标是生成供两个线程使用的受保护代码,则使用信号量会比 ReentrantLock 更简单且更不容易出错。但是,如果您的目标是了解 ReentrantLocks 的工作原理(或者如果您的用例以某种方式更适合 ReentrantLocks 而不是从上面的示例中清楚地看到),我鼓励您阅读 Javadoc - https://docs.oracle.com/ javase/7/docs/api/java/util/concurrent/locks/ReentrantLock.html - 它包含有关如何使用它们以及它们的行为方式的有用信息。


查看完整回答
反对 回复 2023-03-23
?
红糖糍粑

TA贡献1815条经验 获得超6个赞

ReentrantLock解锁状态的API将抛出IllegalMonitorStateException - if the current thread does not hold this lock,在您的代码中,这看起来正是正在发生的事情。


这不是ReentrantLocks 的正常用法,但是如果您无论如何都想使用它们来完成此操作,那么一种方法可能是使用以下方法跟踪状态:


private volatile int tracker = 0;

private Lock lock= new ReentrantLock();


public void first(Runnable printFirst) throws InterruptedException {

    lock.lock();

    try {

        printFirst.run();

        tracker++;

    } finally {

        lock.unlock();

    }

}


public void second(Runnable printSecond) throws InterruptedException {

    while(!Thread.currentThread().isInterrupted()) {

        lock.lock();

        try {

            if (tracker == 1) {

                printSecond.run();

                tracker++;

                break;

            }

        } finally {

            lock.unlock();

        }

        Thread.yield();

    }

}


public void third(Runnable printThird) throws InterruptedException {

    while(!Thread.currentThread().isInterrupted()) {

        lock.lock();

        try {

            if (tracker == 2) {

                printThird.run();

                tracker++;

                break;

            }

        } finally {

            lock.unlock();

        }

        Thread.yield();

    }

}

如果您希望它更高效(减少第二个/第三个函数的处理),那么您可能需要查看类似Condition https://docs.oracle.com/javase/7/docs/api/java/util/concurrent的内容/锁/Condition.html


作为旁注,您可以通过为每个操作要求/释放多个许可来仅使用一个信号量来完成此操作。


查看完整回答
反对 回复 2023-03-23
  • 3 回答
  • 0 关注
  • 66 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信