首页 > 解决方案 > 为什么 Semaphore 可以工作,而 ReentrantLock 不行?

问题描述

我正在尝试 Leetcode 的一个简单的并发问题。我在大学里非常简短地研究过这个主题,但没有使用 Java API。似乎我不能使用 a ReentrantLock(或据我所知的任何其他Lock方法)来解决问题而不会遇到IllegalMonitorStateException. 然而 a Semaphore(这似乎有点矫枉过正,因为我只需要使用二进制值)似乎工作正常。为什么是这样?

Binary Semaphore vs ReentrantLock表明(如果我理解正确的话)二进制锁只能由获取它的线程释放,这可能是我的问题的根源,因为我在下面代码的构造函数中获取它们。有没有其他自然的方法可以使用锁/不使用信号量来解决这个问题?

带有Locks 的代码会引发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 first(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 {}
    }
}

带有 s 的代码Semaphore按预期工作:

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 {}
    }
}

标签: javaconcurrency

解决方案


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

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


推荐阅读