Is it possible to call the main thread from a worker thread in Java?

5.5k views Asked by At

I have a method called action() that deploys three threads. Each deployed thread or worker thread falls into a while loop based on a single instance variable of type boolean being true, for example boolean doWork = true, each thread will have a while(doWork){} loop.

When a thread finishes the job will set the doWork to false stopping all the threads from looping. Then I would like to be able to somehow let the main thread recall the action() method to redeploy the threads to do another job. (If I use one of the worker threads to call the action() method is it OK ?) will the worker thread terminate once it calls the action() method and somehow die ?

I limited the example to two threads for simplicity

Thanks

class TestThreads{
    boolean doWork = true;

    void action(){
         ThreadOne t1 = new ThreadOne();
         ThreadTwo t2 = new ThreadTwo();
    }

    //innerclasses

    class ThreadOne implements Runnable{
          Thread trd1;
          public ThreadOne(){//constructor
              if(trd1 == null){
                   trd1 = new Thread(this);
                   trd1.start();
              }
          }
          @Override
          public void run(){
              while(doWork){
                   //random condition
                   //would set doWork = false;
                   //stop all other threads
              }
              action();//is the method in the main class
          }
    }
    class ThreadTwo implements Runnable{
          Thread trd2;
          public ThreadTwo(){//constroctor
              if(trd2 == null){
                   trd2 = new Thread(this);
                   trd2.start();
              }
          }
          @Override
          public void run(){
              while(doWork){
                   //random condition
                   //would set doWork = false;
                   //stop all other threads
              }
              action();//is the method in the main class
          }
    }

}
5

There are 5 answers

0
Tudor On BEST ANSWER

How about this implementation:

Declare a class member doWork, a counter for currently active threads and a synchronization object:

private volatile boolean doWork = true;
private AtomicInteger activeThreads;
private Object locker = new Object();

In main:

while(true) {
    // call action to start N threads
    activeThreads = new AtomicInteger(N);
    action(N);
    // barrier to wait for threads to finish
    synchronized(locker) {
       while(activeThreads.get() > 0) {
           locker.wait();
       }
    }
}

In thread body:

public void run() {
   while(doWork) {
      ...
      // if task finished set doWork to false
   }

   // signal main thread that I've finished
   synchronized(locker) {
      activeThreads.getAndDecrement();
      locker.notify();
   }
}
0
babernathy On

It's a little difficult to give you an exact solution without any code. It sounds like you are describing the producer/consumer pattern where you give a set of worker threads some tasks and when they are done, you give them more.

Here is a web page that does an OK job of describing what to do.

Also take a look at the ExecutorService that allows you to submit Runnables and have them executed.

0
calebds On

A simple solution is to have the main thread sleep:

static boolean doWork = true; // better to use AtomicBoolean

void action() {

    // start workers, which eventually set doWork = false

    while (doWork) {    
        Thread.sleep(/**time in millis**/); // main thread waits for workers
    }

    // logic to run action() again, etc.

}

The main thread starts the workers, periodically waking up to check if they've terminated. Since the main thread is an "arbiter", it probably shouldn't die just to be resurrected by one of its children.

Reference

0
user949300 On

Skeleton code

// OP said 3 threads...
ExecutorService xs = Executors.newFixedThreadPool(3);

...

// repeat the following as many times as you want...
// this is the setup for his 3 threads - as Callables.

ArrayList<Callable<T>> my3Callables = new ArrayList<Callable<T>>();
my3Callables.add(callable1);
my3Callables.add(callable2);
my3Callables.add(callable3);

try {
   List<Future<T>> futures = xs.invokeAll(my3Callables );

   // below code may not be needed but is useful for catching any exceptions
   for (Future<T> future : futures) {
      T t = future.get();
      // do something with T if wanted
   }
}
catch (ExecutionException ee) {
  // do something
}
catch (CancellationException ce) {
  // do something
}
catch (InterruptedException ie) {
  // do something
}
0
Nim On

I'll expand my comment (even though @babernathy as added this to his answer).

Typically where you have a pool of threads where you want to execute some piece of work, and you have a main thread managing the items of work that you want done, the ExecutorService provides the ideal framework.

In your main object, you can create an instance of the service (with the number of threads you want), and then as you generate a piece of work, submit it to the service, and the service will pick the next available thread from the pool and execute it.

If you have a dependency on knowing if particular pieces of work have completed, you can use something like a CountDownLatch to track when threads have completed their work. My point, there are quite a few existing frameworks for this kind of activity, no need to go through the pain all over again...