【问题标题】:Dining Philosophers using semaphores使用信号量的哲学家用餐
【发布时间】:2014-11-23 01:25:25
【问题描述】:

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

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();
    }

【问题讨论】:

  • 我知道它是如何工作的,我正在寻找使用信号量来解决。
  • @Strobes 新代码可能会陷入死锁,请参阅我的答案的编辑以了解防止这种情况的方法
  • 哦,谢谢您的回复。如果我没记错的话,另一种可能的方法是让其中一位哲学家右撇子?从而消除死锁。只是一个想法。
  • @Strobes 是的,防止死锁的一种通用方法是为每个资源分配唯一的优先级,并要求先获取高优先级资源,然后再获取低优先级资源;在这种情况下,您将为 N 根筷子中的每根分配一个从 1 到 N 的优先级,因此除了一位哲学家之外,左边的筷子将具有优先级 i,而右边的筷子将具有优先级 i+1(即除了一位哲学家以外,其他人都是左撇子),但一位哲学家的左筷子优先级为 N,右筷子优先级为 1(即一位哲学家是右撇子)

标签: java multithreading semaphore


【解决方案1】:

我建议不要在Philosopher 上放置一个信号量,而是在Chopstick 上放置一个信号量; Philosopher 调用获取左右筷子的信号量,完成后释放筷子的信号量。这将替换 Chopstick 上的 ReentrantLock。

为了防止死锁,你可以使用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();
    }
  }
}

【讨论】:

  • 我会尝试这个并尽可能发布结果。
  • 这是正确的。 Semaphore 必须应用于共享资源。 Chopstick 是共享的,而不是 Philosopher。请记住,问题是两个Philosopher 对象试图访问同一个资源; Chopstick。如果您让每个Philosopher 来决定何时可以获取Chopstick,那么每个人很可能会贪婪并拒绝访问它(每个Philosopher 都想继续吃东西)。如果您让资源决定何时可以使用,问题就解决了。
猜你喜欢
  • 2018-06-02
  • 2018-01-21
  • 2017-04-04
  • 2011-07-09
  • 2010-10-11
  • 2015-10-14
  • 2016-03-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多