2015-07-23 4 views
1

Сегодня я решаю попытаться решить проблему обеденных философов. Поэтому я пишу код ниже. Но я думаю, что это неверно, поэтому я буду рад, если кто-то скажет мне, что с ним не так. Я использую forks для блокировок (я их только читаю, из-за этого я не добавляю к ним доступ в синхронизированных блоках), у меня есть класс, который расширяет поток, и он сохраняет свои два замка.Обеденные философы в java

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

Существует имя вашей проблемы: оно называется нитью «голодание». Это то, что происходит, когда несколько потоков конкурируют за один и тот же ресурс, и один (или более) из них постоянно лишен доступа.

Выяснение того, как избежать взаимоблокировок, является одним из аспектов головоломки «Обеденные философы», но выяснение того, как позволить каждому философу получить достаточное количество времени на еду, может быть другим аспектом.

Ответ Дж. М. Морсмау предположил, что вы вынуждаете каждого философа совершать перерыв (часто называемый «мышлением» в классической головоломке), чтобы другие философы получили возможность поесть. Это работает, но если вы думаете о своих философах как о рабочих потоках в каком-либо приложении, то «есть» соответствует рабочей нити, выполняющей что-то полезное, а «покоя» или «мышление» соответствует потоку, сидящему без дела, что может быть чем-то, что вы «Не хочу этого.

Требуется больше, чем просто замки, чтобы убедиться, что все философы получают достаточную долю времени на еду, если все они всегда голодны.

Подсказка: объекты синхронизации более высокого уровня, которые обеспечивают любую гарантию «справедливости», часто используют очереди в реализации.

+0

Большое спасибо. Вы правы, речь идет не о правильной синхронизации. Я знаю, что такое голод, но я никогда не видел такого сильного выражения голода. Спасибо! – DPM

0

Вы запираетесь, когда философ ел, на случайное время, но затем вы постоянно зацикливаетесь, поэтому к тому времени, когда другие потоки будут уведомлены о том, что замок был снят, ваш первый философ снова начал есть. Если я изменить код, чтобы ждать случайно после еды вне замка:

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

Я вижу все философы лучше идти в пищу каждый в свою очередь.

+0

Спасибо, но я не хочу спать после каждого требования. – DPM