在 Java 中用餐哲学家

Posted

技术标签:

【中文标题】在 Java 中用餐哲学家【英文标题】:Dining philosophers in java 【发布时间】:2015-10-14 07:42:17 【问题描述】:

今天,我决定尝试解决哲学家进餐问题。所以我写了下面的代码。但我认为这是不正确的,所以如果有人告诉我它有什么问题,我会很高兴。我使用 forks 锁(我只阅读它们,因为我不会在同步块中访问它们),我有扩展线程的类并保留它的两个锁。

import java.util.Random;

public class EatingPhilosophersProblem 

private final static Random RANDOM = new Random();

/**
 * 
 * @author Damyan Class represents eating of every philosopher. It
 *         represents infinity cycle of eating.
 */
private static class PhilosopherEating extends Thread 

    int forkOne;
    int forkTwo;

    public PhilosopherEating(String name, int forkOne, int forkTwo) 
        super(name);
        this.forkOne = forkOne;
        this.forkTwo = forkTwo;
    

    @Override
    public void run() 
        super.run();

        while (true) 
            requireLock(this, forkOne, forkTwo);
        
    



private static Boolean[] forks = new Boolean[]  new Boolean(true), new Boolean(true), new Boolean(true),
        new Boolean(true), new Boolean(true) ;
// locks should be created by new, otherwise almost 100% sure that they will
// point to the same object (because of java pools)
// this pools are used from java for immutable objects

private static void requireLock(PhilosopherEating philosopherEating, int firstIndex, int secondIndex) 

    // we lock always from the the lower index to the higher, otherwise
    // every philosopher can take his left fork and deadlock will apear

    if (firstIndex > secondIndex) 
        int temp = firstIndex;
        firstIndex = secondIndex;
        secondIndex = temp;
    

    if (firstIndex == 4 || secondIndex == 4) 
        System.err.println(firstIndex + " and " + secondIndex);
    

    synchronized (forks[firstIndex]) 
        synchronized (forks[secondIndex]) 

            printPhilosopherhAction(philosopherEating, "start eating");

            try 
                Thread.sleep(RANDOM.nextInt(100));
             catch (InterruptedException e) 
                e.printStackTrace();
            

            printPhilosopherhAction(philosopherEating, "stop eating");

        
    
;

private static void printPhilosopherhAction(PhilosopherEating philosopherEating, String action) 
    System.out.println("Philosopher " + philosopherEating.getName() + " " + action);



public static void main(String[] args) 

    PhilosopherEating first = new PhilosopherEating("1 - first", 0, 1);
    PhilosopherEating second = new PhilosopherEating("2 - second", 1, 2);
    PhilosopherEating third = new PhilosopherEating("3 - third", 2, 3);
    PhilosopherEating fourth = new PhilosopherEating("4 - fourth", 3, 4);
    PhilosopherEating fifth = new PhilosopherEating("5 - fifth", 4, 0);

    first.start();
    second.start();
    third.start();
    fourth.start();
    fifth.start();


我认为有些不对劲,因为第五位哲学家从不吃东西,而第四位和第三位哲学家大多是在吃东西。 提前致谢。

【问题讨论】:

【参考方案1】:

您的问题有一个名称:它称为线程“饥饿”。当多个线程竞争同一个资源时会发生这种情况,其中一个(或多个)不断被拒绝访问。

弄清楚如何避免僵局是哲学家进餐难题的一个方面,但弄清楚如何让每个哲学家都有相当多的进餐时间可能是另一个方面。

JP Moresmau 的回答建议你强迫每个哲学家休息一下(在经典谜题中通常称为“思考”),以便其他哲学家轮到吃饭。那行得通,但是如果您将哲学家视为某些应用程序中的工作线程,那么“吃”对应于工作线程做一些有用的工作,而“休息”或“思考”对应于闲置的线程,这可能是你我想避免。

如果所有哲学家都总是饿着,那么要确保所有哲学家都获得公平的吃饭时间,需要的不仅仅是锁。

这里有一个提示:做出任何“公平”保证的更高级别的同步对象通常在实现中使用队列。

【讨论】:

非常感谢。你是对的,这与正确同步无关。我知道饥饿是什么,但我从未见过如此强烈的饥饿表情。谢谢!【参考方案2】:

您在哲学家吃饭时锁定了随机时间,但随后您不断循环,因此当通知其他线程已解除锁定时,您的第一个哲学家又开始吃饭了。如果我修改你的代码在锁外吃完后随机等待:

requireLock(this, forkOne, forkTwo);
try 
  Thread.sleep(RANDOM.nextInt(100));
 catch (InterruptedException e) 
  e.printStackTrace();

我看到所有的哲学家都更擅长轮流吃饭。

【讨论】:

谢谢,但我不想在每次要求后睡觉。

以上是关于在 Java 中用餐哲学家的主要内容,如果未能解决你的问题,请参考以下文章

如何在 C++ 中增加信号量值,以解决哲学家用餐问题

如何防止用餐哲学家c ++中的死锁

使用二进制信号量用餐哲学家

哲学家就餐死锁问题及解决方案

线程高级

线程高级.md