Обновление многопоточных ячеек на основе значения соседа. Как продолжить с CyclicBarrier? - PullRequest
0 голосов
/ 02 мая 2019

Я пытаюсь сделать следующее:

  1. Получить два ввода от пользователя (length и amountOfCycles)
  2. Создать массив, содержащий length количество потоков. Каждый из которых содержит целое число value в диапазоне [1, 100].
  3. Зацикливать количество раз amountOfCycles + 1 и выполнять следующую итерацию:
    1. Вывести значения массива.
    2. Обновить каждое значение в массиве на основе его (циклических) соседей:
      • Если значение меньше значения для обоих соседей: увеличьте значение на 1
      • Если значение больше, чем у обоих соседей: уменьшите значение на 1
      • Если текущее значение меньше или равно одному соседу и больше или равно другому соседу: оставьте значение без изменений

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

Вот мой код:

import java.util.Arrays;
import java.util.Scanner;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class Main{
  Cell[] cells;
  CyclicBarrier barrier;
  int length, amountOfCycles;

  Main(){
    Scanner stdin = new Scanner(System.in);
    length = stdin.nextInt();
    amountOfCycles = stdin.nextInt();
    barrier = new CyclicBarrier(length);
    cells = new Cell[length];
    for(int i=0; i<length; i++)
      cells[i] = new Cell(i);
  }

  public static void main(String[] args){
    Main program = new Main();
    program.start();
  }

  void start(){
    for(int i=0; i<length; i++)
      cells[i].run();

    for(int cycle = amountOfCycles; cycle >= 0; cycle--)
      System.out.println(Arrays.toString(cells));
  }

  class Cell implements Runnable{
    int value,
        index;

    Cell(int i){
      index = i;
      value = (int)(Math.random() * 100) + 1; // Random integer within the range [1, 100]
    }

    @Override
    public void run(){
      try{
        // Wait for the start of the cycle:
        barrier.wait();

        // Determine the increment for the value of this cell:
        // Get the values of the neighbors:
        int valueLeftNeighbor = cells[(length - index - 1) % length].value,
            valueRightNeighbor = cells[(index + 1) % length].value,
        // And create an increment-integer with default value 0:
            increment = 0;
        // If the current value is smaller than that of both neighbors:
        if(value < valueLeftNeighbor && value < valueRightNeighbor){
          // Increase the current value by 1
          increment = 1;
        }
        // If the current value is larger than that of both neighbors:
        if(value > valueLeftNeighbor && value > valueRightNeighbor){
          // Decrease the current value by 1
          increment = -1;
        }
        // If the current value is smaller than or equal to one neighbor,
        // and larger than or equal to the other neighbor:
        //  Leave the value the same (so increment stays 0)

        // Wait until every cell is done calculating its new value:
        barrier.await();

        // And then actually update the values of the cells
        value += increment;
      }catch(Exception ex){
        System.err.println("Exception occurred! " + ex);
        ex.printStackTrace();
      }
    }

    @Override
    public String toString(){
      return Integer.toString(value);
    }
  }
}

На основе этого SO вопроса и ответа и принятого ответа .

Что мой код выше делает в настоящее время:

Он печатает массив со случайными значениями amountOfCycles + 1 раз, но не меняет значения между циклами. Это связано с IllegalMonitorStateExceptions, который я получаю. Возможно, потому что мне нужно где-то synchronized(barrier){ ... }, потому что barrier находится в классе Main вместо Cell? Однако добавление его к run -методу класса Cell приводит к тому, что программа больше ничего не печатает и не завершает ..

Здесь в моем коде выше в онлайн-компиляторах, чтобы увидеть текущий (неправильный) результат.

Что я ожидаю от него:

Изменять значения в массиве после каждого цикла.

Ответы [ 2 ]

2 голосов
/ 02 мая 2019

давайте рассмотрим ваши рассуждения:

Выпуск 1

Чтобы вызвать wait () для любого объекта, текущий поток должен иметь свой монитор.Вы вызываете барьер.wait () без каких-либо синхронизированных (барьер).

Вот почему вы получаете IllegalMonitorStateException

Выпуск 2

Добавление синхронизированного раздела приводит к зависанию вашей программы, потому что вы не создаете никаких потоков.Вызов run в Runnable выполняет его синхронно в том же потоке.Нет другого потока для вызова notify

Issue 3

Вы, вероятно, не хотите звонить Object.wait, но CyclicBarrier.await().Таким образом, обсуждение синхронизации, требуемой Object.wait(), не является частью желаемого решения, я добавил его только для пояснения.

1 голос
/ 02 мая 2019

Есть несколько проблем.

1) Вы не создаете темы. Вы можете создавать темы из Runnable следующим образом:

Thread t = new Thread(runnable); //create thread
t.start(); //start the thread

измени свой код:

for(int i=0; i<length; i++)
  cells[i].run();

Примерно так:

for (int i = 0; i < length; i++)
  new Thread(cells[i]).start();

2) Вы не печатаете массив после каждого цикла, вы фактически не реализуете цикл, чтобы иметь цикл. Чтобы напечатать массив после каждого цикла, создайте новый Runnable, который будет вызываться, когда все потоки достигнут циклического барьера, вы можете напрямую установить этот Runnable в циклический барьер

ТАК измените свой код:

Scanner stdin = new Scanner(System.in);
length = stdin.nextInt();
amountOfCycles = stdin.nextInt();
barrier = new CyclicBarrier(length);
cells = new Cell[length];
for(int i=0; i<length; i++)
  cells[i] = new Cell(i);

Примерно так:

Scanner stdin = new Scanner(System.in);
length = stdin.nextInt();
amountOfCycles = stdin.nextInt();

cells = new Cell[length];
for (int i = 0; i < length; i++)
  cells[i] = new Cell(i);

barrier = new CyclicBarrier(length, () -> {
  System.out.println(Arrays.toString(cells)); //code that will run every time when all thread reach the cyclic barrier
});

3) Создать цикл в темах:

Измените свой код:

try{
  // Wait for the start of the cycle:
  barrier.wait(); //remove this, you never called notify so its useless

  //business logic omitted

  // Wait until every cell is done calculating its new value:
  barrier.await();

  // And then actually update the values of the cells
  value += increment;
}catch(Exception ex){
  System.err.println("Exception occurred! " + ex);
  ex.printStackTrace();
}

Примерно так:

int cycleCounter = 0;
while (cycleCounter < amountOfCycles) {
  cycleCounter++;
  try {
    //business logic omitted

    barrier.await();

    // And then actually update the values of the cells    
    value += increment;
  } catch (Exception ex) {
    System.err.println("Exception occurred! " + ex);
    ex.printStackTrace();
  }
}
...