java reuse an executor
Asked Answered
V

4

6

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?

Vermiform answered 27/1, 2012 at 15:51 Comment(0)
O
7

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.

Ovoviviparous answered 27/1, 2012 at 16:3 Comment(4)
you haven't shown how you wait for all the tasks to complete, before moving onto the next step.Fontanez
@Fontanez - invokeAll waits for all tasks to be complete.Ovoviviparous
since he is using execute(Runnable) which does not return a value, we can go for a Callable<Void> perhaps ?Olnek
@Olnek - I normally like to assume some kind of return type, but yes, he definitely could.Ovoviviparous
S
1

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.

Scratches answered 27/1, 2012 at 15:57 Comment(0)
M
1

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();
    }        
}
Modigliani answered 27/1, 2012 at 15:58 Comment(2)
I wouldn't say that creating an Executor backed by an ThreadPool has a minimal overhead...Briolette
It depends how many threads he is using. If the number is small, then there is not much overhead. Also, if the tasks he is executing are coarse grain, it's negligible with respect to the rest of the application.Modigliani
B
1

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.

Briolette answered 27/1, 2012 at 16:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.