我正在努力学习餐饮哲学家问题中信号量的基本要点。现在,我有一个类Chopstick,每个Chopstick都有一个信号量,有1个可用的许可证:
public class Chopstick
{
Thread holder = null;
private Semaphore lock = new Semaphore(1);
public synchronized void take() throws InterruptedException
{
this.lock.acquire();
holder = Thread.currentThread();
}
public synchronized void release()
{
this.lock.release();
holder = null;
}
}
持有人变量用于我不确定需要的功能:
public synchronized void conditionalRelease()
{
if (holder == Thread.currentThread())
{
holder = null;
this.lock.release();
}
}
该程序编译并运行,但似乎在释放筷子时遇到一些麻烦。有时,筷子会被释放,有时则不会。当他们不释放时,当所有的筷子被拿走并且一个哲学家饿了时,程序最终会挂断。
这是Philosopher类中的代码,用于在随机的时间之后释放筷子:
System.out.println(this.name + " is eating");
Thread.sleep(this.getRandTime());
System.out.println(this.name + " has finished eating");
rightChopstick.release();
System.out.println(this.name + " has released the right chopstick");
leftChopstick.release();
System.out.println(this.name + " has released the left chopstick");
例如,我的程序输出“Philosopher 0吃完了”,然后继续执行。其他两行从不输出,所以我发布的方式显然有些不对。
感谢任何帮助。
答案 0 :(得分:8)
我会从您的方法签名中取出'synchronized'关键字。您正在使用外部锁定机制(在本例中为信号量)。 'synchronized'关键字试图使用对象自己的互斥锁获取锁。您现在锁定了2个我怀疑可能导致死锁的资源。
答案 1 :(得分:1)
问题在于,当thread1有一个特定的筷子而另一个试图获得相同的筷子时,它会在take()
- 方法上等待this.lock.acquire();
,但它会不释放对象本身的监视器。
如果现在thread1尝试释放筷子,它就无法进入release()
- 方法,因为它仍然被等待take()
的其他线程锁定。这是一个僵局
答案 2 :(得分:1)
看起来有点令人困惑的是你要锁定筷子并让它持有大小为1的信号量。通常信号量提供资源的门票,如果你只有一张票,那么这实际上是相互排斥的,这与锁(同步块或锁定对象)。您可能会考虑实际上将Chopstick作为锁定对象本身。
如果你感兴趣的话,我曾经在Java上写过一篇关于Java的餐饮哲学家的博客文章,尽管它确实是关于如何通过使用其他策略来避免死锁。
答案 3 :(得分:1)
确保没有使用任何锁定或synchronized关键字。筷子下面的代码对我来说很好。不是专业人士,但必须给你一些想法;
public class Chopstick {
private boolean inuse;
Semaphore sem;
public Chopstick(){
inuse = false;
sem = new Semaphore(1);
}
public void pickUp()
{
try
{
while(inuse)
{
try
{
sem.acquire();
}
catch(InterruptedException e) {}
}
inuse = true;
}catch(Exception e){}
}
public void putDown()
{
try
{
inuse = false;
sem.release();
}
catch (Exception e){}
}
}
答案 4 :(得分:0)
Philospher在开始进食之前需要锁定两个chosticks并且先将leftone先取出然后等待,所以开始进食所以start方法应该同步。 以下方法将使其工作:
public synchronized void startEating() {
leftChopstick.acquire();
rightChopstick.acquire();
}
public void finishEating(int id) {
leftChopstick.release();
rightChopstick.release();
}