Java 监视器而不是二进制信号量

Posted

技术标签:

【中文标题】Java 监视器而不是二进制信号量【英文标题】:Java monitor instead of binary semaphore 【发布时间】:2013-09-18 13:34:17 【问题描述】:

我正在做一个学校作业,我应该使用监视器同步两个线程。在这种情况下,每台监视器都控制一条铁路的通行,而火车需要锁定那条铁路,以便其他人无法访问它或必须等到那条铁轨空闲。我以前从未使用过显示器,所以我确信问题在于我对显示器工作原理的了解有限。火车和它们的线程本身工作得很好,我已经在同一代码中成功地使用了二进制信号量。现在我正在尝试用监视器替换信号量。

我基本上想知道条件和锁是如何工作的。我在不同的博客和论坛上阅读过,但似乎无法理解这个概念。

重要提示:我不允许使用synchronized 关键字。

当我运行当前代码时,我收到以下错误。错误发生在leave方法中的occupied.signal()

Exception in thread "Thread-0" java.lang.IllegalMonitorStateException at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.signalAll(AbstractQueuedSynchronizer.java:1956)

这是目前为止的代码:

public class Monitor 

private final Lock lock = new ReentrantLock();
private final Condition occupied = lock.newCondition();

private boolean isOccupied = false;

private int id;

public Monitor(int id) 
    super();
    this.id = id;


public void enter()
    lock.lock();
    try 
        if(isOccupied)
            occupied.await();
     catch (InterruptedException e) 
        e.printStackTrace();
    
    isOccupied = true;


public boolean tryEnter()
    if(isOccupied)
        return false;
    else
        enter();
        return true;
    


public void leave()
    lock.unlock();
    isOccupied = false;
    occupied.signal();



我将非常感谢任何关于错误的帮助和/或想法。

谢谢!

【问题讨论】:

leave()中,将lock.unlock()移到最后一行。 【参考方案1】:

你的锁定太粗了。作为一般模式,除非您有非常特殊的情况,否则所有锁定都应采用以下形式:

lock.lock();
try 
    ....
 finally 
     lock.unlock();

您没有使用此模式(甚至以不同的方法锁定和解锁)。

从技术上讲,您的问题是当您没有拿着 lock 监视器时,您正在发出 occupied 条件的信号。

在您的程序中,轨道部分的“独占”锁定不应该是实际的 Java 锁定机制,而是布尔变量 isOccupied。更改您的代码,以便这两种方法执行正确的 try...finally 块,并且您可能应该将 Condition 重命名为“未占用”,并反转持有它的逻辑。

public void enter()
    lock.lock();
    try 
        while(isOccupied)
            unoccupied.await();
        isOccupied = true;
     catch (InterruptedException e) 
        e.printStackTrace();
     finally 
        lock.unlock();
    



public void leave()
    lock.lock();
    try 
        isOccupied = false;
        unoccupied.signal();
     catch (InterruptedException e) 
        e.printStackTrace();
     finally 
        lock.unlock();
    

【讨论】:

【参考方案2】:

您不必实现自己的监视器。锁定是监视器:

public class Monitor 

    private final Lock lock = new ReentrantLock();

    private int id;

    public Monitor(int id) 
        super();
        this.id = id;
    

    public void enter()
        lock.lock();
    

    public boolean tryEnter()
        return lock.tryLock();
    
    public void leave()
         lock.unlock();
    

【讨论】:

以上是关于Java 监视器而不是二进制信号量的主要内容,如果未能解决你的问题,请参考以下文章

在队列的关键部分使用二进制信号量而不是互斥锁有啥优势吗?

监视器与互斥体

当一个进程写入而另一个进程读取时,我是不是需要信号量?

使用互斥锁和条件变量而不是信号量在 c++14 中打印从 1 到 10 的数字?

关键部分与计数信号量有什么关系?

使用监视器编写信号量