问题描述
我有一个用例,我正在为用户实现对 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();
}