Что не так с этим алгоритмом выполнения в Java?

Рассмотрим следующее дерево:

дерево

То, что я пытаюсь сделать, это эмулировать древовидный алгоритм, чтобы, если узел получил токен от всех, кроме одного из его напрямую подключенных соседей, он отправляет токен этому тихому соседу (всегда верно для конечного узла). И если узел получает токен от тихого соседа, решение принимается. Узлы всегда 7, и древовидная структура одинакова, поэтому я всегда знаю соседей каждого узла (напрямую соединенные узлы).

Псевдокод алгоритма дерева:

Алгоритм дерева

У меня есть следующий объект:

public final class Node implements Runnable {

    private final int name;

    // Indicating token receiving from neighbours
    private Map<Node, Boolean> neigh = new 
            HashMap<Node, Boolean>();

    public Node(int name) {
        this.name = name;
    }

    public int getName() {
        return name;
    }

    public void addNeigh(Node node) {
        neigh.put(node, false);
    }

    private int flag() {
        Collection<Boolean> c = neigh.values();
        int count = 0;
        for (Boolean bool : c) {
            if (!bool) {
                count++;
            }
        }
        return count;
    }

    private Node getSilentNeigh() {
        for (Entry<Node, Boolean> entry : neigh.entrySet()) {
            if (false == entry.getValue()) {
                return entry.getKey();
            }
        }
        return null;
    }

    public void sendToken(Node from, String token) {

        Node n;
        if ((n = getSilentNeigh()) != null && flag() == 1) {
            if (from.equals(n)) {
                System.out.println(name + " decides!");
            }
        }

        neigh.put(from, true);
    }

    @Override
    public boolean equals(Object obj) {

        if (this == obj) {
            return true;
        }

        if (!(obj instanceof Node)) {
            return false;
        }

        final Node n = (Node) obj;
        return name == n.name;
    }

    @Override
    public int hashCode() {
        int hc = 17;
        return 37 * hc + name;
    }

    @Override
    public void run() {
        while(flag() > 1);

        Node n = getSilentNeigh();

        System.out.println(name + " sends token to " + n.getName());

        n.sendToken(this, "token");
    }

    @Override
    public String toString() {
        return "Node " + name;
    }
}

Внутри метода run() есть время (условие), которое на самом деле означает.. ожидание (получение токенов от соседей), и когда есть только один сосед, узел не получил токен, чтобы отправить ему токен.

Вот как я создаю узлы и как я ассоциирую друг друга:

        // Number of nodes
        int numberOfNodes = 7;

        // Array of nodes
        Node[] nodes = new Node[numberOfNodes]; 

        for (int i = 0; i < nodes.length; i++) {
            // Creating node
            nodes[i] = new Node(i);
        }

        nodes[0].addNeigh(nodes[1]);
        nodes[0].addNeigh(nodes[2]);
        nodes[1].addNeigh(nodes[0]);
        nodes[1].addNeigh(nodes[3]);
        nodes[1].addNeigh(nodes[4]);
        nodes[2].addNeigh(nodes[0]);
        nodes[2].addNeigh(nodes[5]);
        nodes[2].addNeigh(nodes[6]);    
        nodes[3].addNeigh(nodes[1]);
        nodes[4].addNeigh(nodes[1]);
        nodes[5].addNeigh(nodes[2]);
        nodes[6].addNeigh(nodes[2]);

То, что я делаю, это случайный выбор порядка узлов для выполнения:

        // List holding random node numbers
        List numbers = new ArrayList<Integer>();

        int chosen = 0;
        while (chosen < numberOfNodes) {
            int processNum = randInt(0, (numberOfNodes - 1));
            if (!numbers.contains(Integer.valueOf(processNum))) {
                 numbers.add(new Integer(processNum));
                 chosen++;
            }
        }

Так, например, узлы могут быть в любом порядке:

0, 5, 3, 4, 6, 2, 1
5, 3, 0, 2, 1, 6, 4
3, 1, 0, 2, 4, 6, 5

а потом я запускаю темы:

for (Integer number : numbers) {
    Thread thread = new Thread(nodes[number]);
    thread.start();
}

Иногда я получаю ожидаемый результат (2 должны решить):

Nodes selected: 0, 4, 5, 2, 6, 3, 1
5 sends token to 2
4 sends token to 1
6 sends token to 2
3 sends token to 1
1 sends token to 0
0 sends token to 2
2 decides!
2 sends token to 0
0 decides!

Но обычно я получаю ошибку, и только один решает:

Nodes selected: 5, 3, 4, 6, 0, 2, 1
3 sends token to 1
5 sends token to 2
4 sends token to 1
6 sends token to 2
2 sends token to 0
0 sends token to 1
1 decides!
Exception in thread "Thread-6" java.lang.NullPointerException
    at uk.ac.ncl.csc8103.wave.Node.run(Node.java:86)
    at java.lang.Thread.run(Thread.java:745)

И да, это для Задания, и я очень близок к этому, ребята... но я сталкиваюсь с этой проблемой.

2 ответа

Ну твой run вызовы методов Node n = getSilentNeigh(); Этот вызов может вернуть ноль, и вы используете n переменная без проверки, что это не ноль.

@Override
public void run() {
    while(flag() > 1);
    Node n = getSilentNeigh(); // this call can return null
    System.out.println(name + " sends token to " + n.getName()); // you are probably getting 
                                                                 // NullPointerException here
    n.sendToken(this, "token");
}
Inside the run() method there is a while(condition) that actually means.. wait (receiving tokens from neighbours)

Это не так в соответствии с вашим кодом

  while(flag() > 1);

если узел имеет только один узел ( 4 /5 / 6) - для них флаг () вернет 1 и завершится в то время как цикл, а затем будет двигаться вперед и отправлять запрос даже до того, как инициаторы начнут отправлять сообщение

ОБНОВИТЬ

Как вы знаете, для этого кода есть 4 инициатора (3,4,5,6) и исключения для выхода из цикла while.

Возьми это

Выбранные узлы: 5, 3, 4, 6, 0, 2, 1 3 отправляет токен на 1 5 отправляет токен на 2 4 отправляет токен на 1 6 отправляет токен на 2 2 отправляет токен на 0 0 отправляет токен на 1 1 решает!

Это просто условие гонки, что если ранее запущенный инициатор (5) уже достиг / отправляет сообщение соседу ( 1) другого инициатора (3). в то время как Инициатор (3) попытается запустить и получить SlientNeigh, он получит Null (без скользкого ржа). таким образом вы получаете исключение нулевого указателя в n.sendToken.

Ссылка на алгоритм: Вам не нужно sycn (у нас нет правила, что каждый узел должен получать только одно сообщение от соседа).

Из ссылки три простых свойства

  – In each computation there is one initiator, which starts the algorithm by sending out exactly one message

–– A process, upon receipt of a message, either sends out one message or decides


–– The algorithm terminates in the initiator and when this happens, each process has sent a message at least once
Другие вопросы по тегам