猿问

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

我正在尝试 Leetcode 中的一个简单的并发问题。我在大学里非常简要地研究过这个主题,但没有使用 Java API。似乎我不能在ReentrantLockLock遇到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 firs

Semaphore带有按预期工作的 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 {}

    }

}


炎炎设计
浏览 91回答 3
3回答

蝴蝶不菲

同步有两个主要用例:资源访问和事件传递。您试图解决的并发问题需要事件传递:第二个线程等待第一个线程的信号,第三个线程等待第二个线程的信号。信号,我的意思是,是空事件,它们带有事件已经发生的事实,没有任何额外的细节。Semaphores 非常适合信号传递(尽管也可用于资源访问)。ReentrantLocks 是为资源访问而设计的。从技术上讲,任何事件传递机制都是建立在资源访问之上的。因此您可以使用ReentrantLocks 进行信号传递,但它需要一些额外的编程(在@billie 的回答中演示)。

森林海

正如您所发现的,信号量易于实现和推理 - 获取锁定、执行操作、释放锁定。ReentrantLocks 是不同的,旨在服务于您发布的示例中不存在的更复杂的目的。是的,ReentrantLocks 由单个线程拥有,只能由该线程释放。如果您的目标是生成供两个线程使用的受保护代码,则使用信号量会比 ReentrantLock 更简单且更不容易出错。但是,如果您的目标是了解 ReentrantLocks 的工作原理(或者如果您的用例以某种方式更适合 ReentrantLocks 而不是从上面的示例中清楚地看到),我鼓励您阅读 Javadoc - https://docs.oracle.com/ javase/7/docs/api/java/util/concurrent/locks/ReentrantLock.html - 它包含有关如何使用它们以及它们的行为方式的有用信息。

红糖糍粑

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作为旁注,您可以通过为每个操作要求/释放多个许可来仅使用一个信号量来完成此操作。
随时随地看视频慕课网APP

相关分类

Java
我要回答