简体   繁体   中英

Update multithreading cells based on neighbor's value. How to continue with CyclicBarrier?

I'm trying to accomplish the following:

  1. Get two inputs from the user ( length and amountOfCycles )
  2. Create an array containing length amount of Threads. Each containing an integer value within the range [1, 100] .
  3. Loop the amountOfCycles + 1 amount of times and do the following every iteration:
    1. Print the values of the array.
    2. Update each value in the array based on its (cyclic) neighbors:
      • If the value is smaller than that of both neighbors: increase the value by 1
      • If the value is larger than that of both neighbors: decrease the value by 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 unchanged

Updating these values based on their neighbors is the reason for it to be multi-threading. Please note that this is just something to practice multi-threading. I'm easily able to do what's described above by simply removing threads all together and create a copy of the array ( which I already did ).

Here is my code thus far:

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

Which is based on this SO question and answer and its accepted answer .

What my code above currently does:

It prints the array with random values amountOfCycles + 1 times, but doesn't change any value in between cycles. This is due to the IllegalMonitorStateExceptions I get. Probably because I need a synchronized(barrier){ ... } somewhere, because the barrier is in the class Main instead of Cell ? Adding it to the run -method of the Cell class however causes the program to not print anything anymore, nor terminate..

Here in my code above in an online compilers to see the current (incorrect) result.

What I expect it to do:

Modify the values in the array after every cycle.

let's go over your reasoning:

Issue 1

In order to call wait() on any object, the current thread has to own its monitor. You're calling barrier.wait() without any synchronized(barrier).

This is why you get IllegalMonitorStateException

Issue 2

Adding a synchronized section causes your program to hang because you don't create any Threads. Calling run on a Runnable executes it synchronously in the same thread. There's no other thread to call notify

Issue 3

You probably don't want to call Object.wait but CyclicBarrier.await() . Thus the discussion of synchronization required by Object.wait() is not a part of the desired solution, I added it only for clarification.

There are a few problems.

1) You are not creating threads. You can create Threads from Runnable like this:

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

change your code:

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

To something like this:

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

2) You don't print the array after every cycle you actually don't implement any loop to have a cycle. To print the array after every cycle create new Runnable, that will be called when all thread reach the cyclic barrier you can directly set this Runnable into the cyclic barrier

SO change your code:

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

To something like this:

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) Create cycle in the threads:

Change your code:

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

To something like this:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM