Search code examples
javamultithreadingjava-threads

Java main thread sending signal and then waiting for other non-synchronized threads


I'm learning about threads in Java, and I'm trying to do something like this:
I have a matrix with values and for each cell in the matrix, there is a thread assigned to it, which updates the values (it's according to conway's game of life). The threads should all update the matrix's values once and then when they're all finished, the main thread will display the updated matrix and then ask the user if it should calculate the next step as well, which means starting the same calculations all over again.
Now, I've written almost all of the base code, including the threads and how they update the cell they're responsible for, but I can't find a way for the main thread to notify all other threads to wake up, do their job, and then go back to sleep, and receive notification from them after they all finished their job for now. I've tried using the wait() and notify()/notifyAll() methods, but they're only for synchronized methods, which I don't need to implement in the code (since I'm using two matrices to make sure that first all of the threads update their respective cell and only then the current matrix will become the next generation matrix).
Here is the run() method for the threads:

public void run(boolean[][] currentGenMatrix) {
    updateLifeMatrix(currentGenMatrix);
    while(true) {
        try {
            wait();    // Wait for the next step before starting
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        updateLifeMatrix(m_nextGenMatrix);    // Update the current matrix to be the "next generation matrix" from the previous generation
        int neighbours = checkNeighbours(currentGenMatrix);
        m_nextGenMatrix[this.m_heightIndex][this.m_widthIndex] = setNewLifeOrDeath(neighbours, getCurrentLifeOrDeath(currentGenMatrix));    // Update the "next generation matrix" cell assigned to this thread
    }
}

During the initialization of the matrix, I start off all of the threads after creating them, and then they first wait for a notification before taking the first step. I've also read about monitors to see if that's what I need, but the examples I've seen online don't fit to my code.


Solution

  • Here is a simple example using AcyclicBarrier, with two worker threads controlled by the coordinator thread (the thread executing the main method). Two barriers are used; all worker threads are waiting on the start barrier. When the coordinator awaits on that barrier, the barrier is broken and the workers start. To coordinate the work finish, all N workers, plus the coordinator must arrive to the end barrier and break it (await on it).

    package stackOv;
    import java.util.concurrent.BrokenBarrierException;
    import java.util.concurrent.CyclicBarrier;
    
    public class UseBarrier {
    
      public static void main(String[] args) throws Exception {
        int numThreads = 2 ;
        // one slot for the coordinator
        CyclicBarrier barrierStart = new CyclicBarrier(numThreads+1);
        CyclicBarrier barrierEnd = new CyclicBarrier(numThreads+1);
    
        Runnable work1 = new WorkerThread(barrierStart, barrierEnd, "work-0");
        Runnable work2 = new WorkerThread(barrierStart, barrierEnd, "work-1");
        new Thread(work1).start();
        new Thread(work2).start();
    
        while (true) {
          System.out.println("main: start");
          Thread.sleep(1000);
          barrierStart.await();
          System.out.println("waiting for workers..");
          barrierEnd.await();
          System.out.println("work finished, continue");
          Thread.sleep(1000);
        }
      }
    }
    
    class WorkerThread implements Runnable {
      private CyclicBarrier startWork;
      private CyclicBarrier endWork;
      private String name;
      public WorkerThread(CyclicBarrier startWork, CyclicBarrier barrier, String name) {
        this.startWork = startWork;
        this.endWork = barrier;
        this.name = name;
      }
    
      @Override
      public void run() {
        while (true) {
          try {
            System.out.println("worker "+name+": waiting");
            startWork.await();
            System.out.println("working.. "+name);
            Thread.sleep(1000);
            System.out.println("worker "+name+": done");
            endWork.await();
          } catch (BrokenBarrierException | InterruptedException e) {
            // depending on your application, you can either manage the
            // brokenBarrier exception, close your program (or ignore it)
          }
        }
      }
    }