控制对共享资源的访问的信号量

问题描述

我有一个用例,我正在为用户实现对 ATM 机的访问,使用 Semaphore 包中的 java.util.concurrent 类作为锁,即锁定对资源的访问。

任何想要访问锁定资源的线程在访问资源之前调用acquire()方法,通过调用release()方法获取锁和释放锁,任务完成后。

实现代码

import java.util.concurrent.Semaphore;

class ATMMachine {
    
    public static void main(String []args) {
        Semaphore machines = new Semaphore(1,true);
        new Person(machines,"A");
        new Person(machines,"B");
        new Person(machines,"C");
    }
}

class Person extends Thread {
    
    private Semaphore machines;
    
    public Person(Semaphore machines,String name) {
        this.machines = machines;
        this.setName(name);
        this.start();
    }
    
    public void run() {
        try {
            System.out.println(getName()+ " is waiting to access the ATM machine");
            machines.acquire();
            System.out.println(getName()+ " is accessing the ATM machine");
            Thread.sleep(1000);
            System.out.println(getName()+ " is done using the ATM machine");
            machines.release();
        } catch(InterruptedException ie) {
            System.err.println(ie);
        }
    }
}

输出

A is waiting to access the ATM machine
C is waiting to access the ATM machine
B is waiting to access the ATM machine
A is accessing the ATM machine
A is done using the ATM machine
C is accessing the ATM machine
C is done using the ATM machine
B is accessing the ATM machine
B is done using the ATM machine

问题:每次运行程序时,输出都会发生变化。但是,我想确保按顺序授予用户对机器的访问权限:A → 然后 B → 最后 C

注意:我在构造函数中将 fair 参数设置为 true,这应该保证线程按照请求的顺序进行 FIFO。但它似乎不起作用。

请您帮助我提出解决问题的任何建议/参考。谢谢!

解决方法

问题在于您没有控制每个线程调用 .acquire 的顺序——您只是启动了 3 个线程,无论哪个线程先调用“公平”.acquire 都将获胜。有时是A,有时是B,有时是C。

另请注意,“FIFO 保证”可能并不像您想象的那么强大 — 本质上并不是关于调用 .acquire 的顺序是谁,而是关于该函数中的一个排序点。

因此,如果您需要 A,然后是 B,然后是 C,则信号量不是正确的解决方案。如果您需要按特定顺序顺序访问资源,那么为什么不简化所有内容并使用单个线程处理所有内容?

,

这种需要保持线程请求访问共享资源的严格顺序的问题,信号量/互斥量无法解决。 您需要实现一个锁定机制,该机制将支持授予对共享资源的访问权限的 FIFO 顺序。让我们称这种锁定机制为 FIFO Mutex。您可以使用 Ticket Locks 实现此 FIFO 互斥锁。

我已经为 FIFO 互斥体实现了伪代码。它可能会帮助你。在这里结帐: https://codeistry.wordpress.com/2018/03/21/fifo-mutex/

FIFO Mutex 类的伪代码:

class FifoMutex
{
public:
 
    FifoMutex();
    ~FifoMutex();
    void
    lock();
    void
    unlock();
 
private:
 
    UINT32 m_nowServing;
    UINT32 m_nextAvailable;
    ConditionMutex *m_condMutex;
};
 
FifoMutex::FifoMutex()
{
    m_nowServing = 0;
    m_nextAvailable = 0;
    m_condMutex = new ConditionMutex();
}
void
FifoMutex::lock()
{
    UINT32 myTicket;
 
    // Get your ticket and increment the m_nextAvailable.
    m_condMutex->lock();
        myTicket = m_nextAvailable;
        m_nextAvailable++;
    m_condMutex->unlock();
 
    // Wait till your ticket is getting served.
    m_condMutex->lock();
        while (m_nowServing != myTicket) {
            m_condMutex->wait();
        }
    m_condMutex->unlock();
}
 
void
FifoMutex::unlock()
{
    // Increment the m_nowServing and wakeup all blocked threads.
    m_condMutex->lock();
        m_nowServing++;
        // Pl note we are broadcasting the wakeup call.
        // We must broadcast since we don't know 
        // which thread should acquire the lock next.
        // All the waiting threads will be unblocked,// only one of them will pass condition: "m_nowServing == myTicket"
        m_condMutex->broadcast();
    m_condMutex->unlock();
}