Question

I work on a simulation system, where at each timestep, I have to simulate many models. I used a FixedThreadPool to speed up the calculation:

ExecutorService executor = Executors.newFixedThreadPool(nThread);
for (Model m : models) {
  executor.execute( m.simulationTask() );
}
executor.shutdown();
while ( ! executor.awaitTermination(10, TimeUnit.MINUTES) ) { 
  System.out.println("wait"); 
}

Now, the executor can't be used to execute() new Tasks after calling shutdown(). Is there a way to reset the executor, so I can reuse the existing executor (and its threads) at the next simulation step?

Was it helpful?

Solution

You can reuse the executor service if you restructure your code somewhat.

Collection<Callable<Integer>> tasks = new ArrayList<Callable<Integer>>(16);
for (Model m : models) {
  tasks.add(m.simulationTask());
}

ExecutorService executor = Executors.newFixedThreadPool(nThread);
try {
  executor.invokeAll(tasks);
} catch(InterruptedException ie) {
  // Handle this
}

Basically you collect all your tasks, execute them, and await execution before proceeding. Of course, you could also alternatively just use a new Executor Service for each of your time steps, but at least you have options.

Caveats: I didn't compile the code so there might be errors. I also assumed an Integer parameter type for convenience.

OTHER TIPS

You can write your own implementation of the Executor interface. Beyond that, most of the default implementation that I am aware of reap threads and do memory cleanup after shutdown(), so there is no (to my knowledge) pre-made solution.

Considering that shutdown() is likely to do a lot of cleanup and garbage collection, it's not exactly clear why restarting would be better than accquiring a new Executor, perhaps you should look into the tutorials about extending ThreadPoolExecutor with a pause / resume set of methods instead of adding the ability to un-shutdown.

Declare your ExecutorService as a member to your class and reuse it as you want. Do not call shutDown() on it as it will not accept any more tasks. Of course your tasks should end nicely and they also should terminate at some point.

Just acquire another ExecutorService. The overhead is minimal anyway.

If you insist on reusing the same executor, you can implement your own barrier mechanism. Upon submitting a new task, atomically increment a counter. When a task finishes, atomically decrement the counter. In the main thread wait until the counter is zero. Something like:

// globally visible objects
AtomicInteger counter = new AtomicInteger(0);
Object signal = new Object();

ExecutorService executor = Executors.newFixedThreadPool(nThread);
for (Model m : models) {
  counter.getAndIncrement();
  executor.execute( m.simulationTask() );
}

synchronized(signal) {
   while(count.get() > 0) {
       signal.wait();
   }
}

Then inside the run of your tasks:

public void run() {
    // original code
    // at the end:
    synchronized(signal) {
       counter.getAndDecrement();
       signal.notify();
    }        
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top