garou
garou

Reputation: 21

java multiple threads running , stop threads when one thread finds solution

I'm having troubles trying to stop my program that has multiple threads running, all threads running are trying to find the same solution but once one thread finds the solution all other threads are to stop.

In the main method I have created a thread group and add threads to it using a for loop and start them

ThreadGroup tg = new ThreadGroup("thread group");
Thread th;
for(int i  = 0; i<4; i++){
  th = new Thread(tg, new Runnable(), "Thread " + i)
  th.start();
}

in the class that implements Runnable I am having troubles trying to figure out how to make it so that once one of the thread finds a solution all the threads will stop. What ends up happening is that either the other threads keep running and sometimes the threads will interupt each other and write over each other.

Upvotes: 2

Views: 1462

Answers (3)

Mike Robinson
Mike Robinson

Reputation: 8955

Upon realizing that a solution has been found, the victorious thread should signal the parent – which then signals all other children to stop, or simply kills them.

Upvotes: 3

NoDataFound
NoDataFound

Reputation: 11959

You have to interrupt those thread (and handle interruption in the runnable). I also not sure if you should use ThreadGroup - I remember seeing a Sonar warning about them.

You would perhaps better have to an ExecutorService and do that using a CountDownLatch (that's one way to do that):

ExecutorService es = Executors.newFixedThreadPool(100);
CountDownLatch cdl = new CountDownLatch(1);
for (int i = 0; i < 100; ++i) {
  es.submit(() -> {
    Thread.sleep(TimeUnit.SECONDS.toMillis(30)); // + exception handling  
    cdl.countDown();
  });
}
cdl.await(); // or await(5, TimeUnit.MINUTES);
es.shutdownNow();

The trick is:

  1. You create an ExecutorService with a pool of 100 threads.
  2. You create a CoundDownLatch - a barrier - with a count of 1.
  3. You submit your task which, when their job is done, invoke cdl.countDown(); reducing the counter from 1 to 0.
  4. The parent thread wait for the CountDownLatch to reduce to 0 - you should probably use the second version (to block until 5 minutes for example).

If all Runnable fails, you won't have a result: either use a maximum await time, either you could add another CountDownLatch, this time with a count of 100 (the number of threads), countDown() in a try/finally, and in another thread, interrupt the one awaiting on the cdl. You could also do that in a loop:

CountDownLatch allCdl = new CountDownLatch(100);

for (;allCdl.getCount() != 0;) {
  if (!cdl.await(60, TimeUnit.SECONDS)) {
    if (allCdl.getCount() == 0) { 
      break;
    }
  }
}

However, the javadoc of getCount() mention that This method is typically used for debugging and testing purposes. (see CyclicBarrier). Not sure if this is the correct usage.

Upvotes: 3

F43nd1r
F43nd1r

Reputation: 7749

ThreadGroup tg = new ThreadGroup("thread group");
CountDownLatch latch = new CountDownLatch(1);
AtomicInteger result = new AtomicInteger();
Random random = new Random();
for (int i = 0; i < 4; i++) {
    Thread th = new Thread(tg, () -> {
        try {
            Thread.sleep(random.nextInt(10000));
            result.set(42);
            latch.countDown();
            System.out.println(Thread.currentThread().getName() + " completed task first");
        } catch (InterruptedException e) {
            System.out.println(Thread.currentThread().getName() + " was interrupted before it could finish the task");
        }
    }, "Thread " + i);
    th.start();
}
while (latch.getCount() > 0) {
    try {
        latch.await();
    } catch (InterruptedException ignored) {
    }
}
tg.interrupt();
System.out.println("The result is " + result.get());

This example shows how to wait until a thread finishes.

You need to make sure your action is interruptible. Thread.sleep as shown in this example is interrubtible by default. See oracle docs for more info.

Also note that it is impossible to guarantee that all other threads will be interrupted before they complete. If you need to make sure to handle only one result, synchronize the access to your result variable and discard any changes beyond the first.

Upvotes: 1

Related Questions