我正在开发一个Java项目来模拟dining philosophers problem。
我从基本行为开始,其中每个线程都是think(),getForks(),eat()和putForks()。因此,没有预防死锁或饥饿(故意进行)。
getForks()方法的工作方式如下:
getForks(){
while(forks[rightFork]==0) /*0 means fork is not on the table, so wait*/
print(Thread #id waiting for right fork);
forks[rightFork] = 0;
while(forks[leftFork]==0)
print(Thread #id waiting for left fork);
forks[leftFork = 0;
}
我在获取右叉和左叉之间进行了一次睡眠(5000),因此程序遇到了死锁(每个线程都持有右叉)。但是,出乎意料的是,由于某些原因,一旦达到死锁,执行就会暂停。我期待"线程#id等待fork"消息会在死锁期间继续打印,但事实并非如此。一旦达到死锁,就不会再打印消息了。
有什么线索?
如果你想看到整个代码,请点击这里:
public class Philosophers{
private static final int NUMBER = 3;
private static final int MIN_SLEEP = 1000;
private static final int MAX_SLEEP = 6000;
private static Thread[] threads = new Thread[NUMBER];
private static int[] forksArray = new int[8];
private static void start(){
System.out.println("Simulation started.");
//Initialize forks array (1 means on the table)
for(int i=0;i<7;i++)
forksArray[i] = 1;
//Create and start individual threads
for(int i=0;i<NUMBER;i++){
threads[i] = new Thread(new Philosopher(i));
threads[i].start();
}
}
public static void main(String[] args){
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
start();
}
});
}
private static class Philosopher implements Runnable{
private int id;
private int leftFork;
private int rightFork;
public void run(){
System.out.println("Thread "+id+" started.");
while(true){
think();
getForks();
eat();
putForks();
}
}
public Philosopher(int id){
this.id = id;
this.rightFork = id;
if(id==NUMBER - 1)
this.leftFork = 0;
else
this.leftFork = id + 1;
}
public void think(){
System.out.println("Thread "+id+" thinking...");
try{
int sleepInterval = MIN_SLEEP + (int)(Math.random() * ((MAX_SLEEP - MIN_SLEEP) + 1));
Thread.sleep(sleepInterval);
}
catch(Exception e){
System.out.println(e);
}
}
public void getForks(){
System.out.println("Thread "+id+" getting forks.");
//Grab fork on the right
while(forksArray[rightFork]==0)
System.out.println("Thread "+id+" waiting for right fork");
forksArray[rightFork] = 0;
try{
Thread.sleep(5000);
}
catch(Exception e){
}
//Grab fork on the left
while(forksArray[leftFork]==0);
System.out.println("Thread "+id+" waiting for left fork");
forksArray[leftFork] = 0;
}
public void eat(){
System.out.println("Thread "+id+" eating.");
try{
Thread.sleep(2000);
}
catch(Exception e){
System.out.println(e);
}
}
public void putForks(){
System.out.println("Thread "+id+" putting forks down.");
forksArray[rightFork] = 1;
forksArray[leftFork] = 1;
}
}
}
答案 0 :(得分:3)
我认为你的问题在这里:
while(forksArray[leftFork]==0);
System.out.println("Thread "+id+" waiting for left fork");
;在while()
之后意味着它有一个空语句作为它的主体。因此,下一行的println
只会在循环终止时发生。
(顺便说一下,这就是为什么人们经常建议总是使用{} for循环,即使它们是单行的)
答案 1 :(得分:2)
这不是真正的僵局:你不使用任何锁!它只是一个无限循环。
一个问题是您没有使用正确的同步。特别是,编译器可以自由替换:
while (forksArray[leftFork] == 0);
使用:
int temp = forksArray[leftFork];
while (temp == 0);
这可能会在您执行程序的任何时候发生。
简单地让你的数组变得不稳定:
private static volatile int[] forksArray = new int[8];
应该让这个问题消失。但请注意,将数组标记为volatile只会阻止执行优化 - 仅使代码线程安全是不够的。