用餐哲学家使用信号量

时间:2014-11-23 01:25:26

标签: java multithreading semaphore

我正在寻找一种使用信号量来解决餐饮哲学家问题的方法,我完全不知道应该如何去做。我在下面提供了我的代码。

class ChopStick{
    private int count; 
    private boolean inuse; 
    Lock lock = new ReentrantLock(); 
    Condition notInUse = lock.newCondition(); 

    public ChopStick(){
        inuse = false;
    }

    public void pickUp(){
        lock.lock();
        try{
            while(inuse){
                try{
                    notInUse.await();
                }catch(InterruptedException e){}
            }
            inuse = true;
        }finally{lock.unlock();}
    }

    public void putDown(){
        lock.lock();
        try{
            inuse = false; 
            notInUse.signal();
        }finally{lock.unlock();}
    }
}

class Philosopher extends Thread{
    Semaphore sem;
    private ChopStick ch1,ch2; //chopsticks
    private int phil; //philosopher id

    public Philosopher(int p, ChopStick left, ChopStick right, Semaphore s){
        phil = p; 
        ch1 = left; 
        ch2 = right; 
        sem = s;
    }

    public void run() {
        while(true){
            try {
                sem.acquire();
            } catch (InterruptedException e) {}
                think(phil);
                //pickup chopsticks
                ch1.pickUp();
                ch2.pickUp();
                eat(phil);
                //putdown chopsticks
                ch1.putDown();
                ch2.putDown();
                sem.release();
            }
        }

我想到哲学家何时使用sem.acquire()拿起筷子然后当他们使用sem.release()时,但我不确定这是否正确。是吗?

编辑所以我已经实现了这一点。它似乎有效,但我不确定。

  class ChopStick{
    private Semaphore sem;
    public ChopStick(Semaphore s){
            sem = s;
    }

    public void pickUp(){
            try{
                    sem.acquire();
            }catch(InterruptedException e){}
    }
    public void putDown(){
            sem.release();
    }

1 个答案:

答案 0 :(得分:4)

我建议在Philosopher上添加一个信号量,而不是在Chopstick上放置信号量; Philosopher来电获取左右筷子'信号量和释放筷子'信号量完成后。这将取代ReentrantLock上的Chopstick

为防止死锁,您可以使用tryAcquire(int permits, long timeout, TimeUnit unit),以便Philosopher释放其左筷子的信号量,如果它无法在超时内获取其正确的筷子的信号量;如果您使用随机超时(例如在100到500毫秒之间),那么每个Philosopher最终都应该取得进展。

编辑:您的新Chopstick代码存在死锁风险 - 所有哲学家都拿起他们的左筷子然后等待他们的正确筷子自由。 tryAcquire允许哲学家释放其左筷子,如果它在超时后无法获得正确的筷子,这将允许哲学家向左移动。

class ChopStick{
  private static Random random = new Random();

  // initialize with one permit
  private Semaphore sem = new Semaphore(1);

  public boolean pickUp(){
    try {
      // wait between 100 and 500 milliseconds
      return sem.tryAcquire(1, random.nextInt(400) + 100, TimeUnit.MILLISECONDS);
    } catch(InterruptedException e) {
      return false;
    }
  }

  public void putDown(){
    sem.release();
  }
}

class Philosopher extends Thread {
  public void run() {
    while(true){
      think(phil);
      doEat();
    }

  private void doEat() {
    if(ch1.pickup()) {
      if(ch2.pickup()) {
        eat(phil);
        ch1.release();
        ch2.release();
      else {
        ch1.release();
        doEat();
      }
    else {
      doEat();
    }
  }
}