I'm working on a Java project where I need to have multiple tasks running asynchronously. I'm led to believe Executor is the best way for me to do this, so I'm familiarizing myself with it. (Yay getting paid to learn!) However, it's not clear to me what the best way is to accomplish what I'm trying to do.
For the sake of argument, let's say I have two tasks running. Neither is expected to terminate, and both should run for the duration of the application's life. I'm trying to write a main wrapper class such that:
Now, it should be noted that the implementation for both tasks will wrap the code in run()
in an infinite loop that will never run to completion, with a try/catch block that should handle all runtime exceptions without disrupting the loop. I'm trying to add another layer of certainty; if either I or somebody who follows me does something stupid that defeats these safeguards and halts the task, the application needs to react appropriately.
Is there a best practice for approaching this problem that folks more experienced than me would recommend?
FWIW, I've whipped-up this test class:
public class ExecTest { private static ExecutorService executor = null; private static Future results1 = null; private static Future results2 = null; public static void main(String[] args) { executor = Executors.newFixedThreadPool(2); while(true) { try { checkTasks(); Thread.sleep(1000); } catch (Exception e) { System.err.println("Caught exception: " + e.getMessage()); } } } private static void checkTasks() throws Exception{ if (results1 == null || results1.isDone() || results1.isCancelled()) { results1 = executor.submit(new Test1()); } if (results2 == null || results2.isDone() || results2.isCancelled()) { results2 = executor.submit(new Test2()); } } } class Test1 implements Runnable { public void run() { while(true) { System.out.println("I'm test class 1"); try {Thread.sleep(1000);} catch (Exception e) {} } } } class Test2 implements Runnable { public void run() { while(true) { System.out.println("I'm test class 2"); try {Thread.sleep(1000);} catch (Exception e) {} } } }
It's behaving the way I want, but I don't know if there are any gotchas, inefficiencies, or downright wrong-headedness waiting to surprise me. (In fact, given that I'm new to this, I'd be shocked if there wasn't something wrong/inadvisable about it.)
Any insight is welcomed.
When finished using an ExecutorService , you need to shut it down explicitly. From its javadoc: "An unused ExecutorService should be shut down to allow reclamation of its resources." Calling shutdown initiates a gradual and orderly shutdown.
Tasks are submitted to a thread pool via an internal queue called the Blocking Queue. If there are more tasks than the number of active threads, they are inserted into the blocking queue for waiting until any thread becomes available.
Executor just executes stuff you give it. ExecutorService adds startup, shutdown, and the ability to wait for and look at the status of jobs you've submitted for execution on top of Executor (which it extends). This is a perfect answer, short and clear.
I faced a similar situation in my previous project, and after my code blew in the face of an angry customer, my buddies and I added two big safe-guards:
For example, we had a situation where the database went down and during the loop an SQLException was thrown. The unfortunate result was that the code went through the loop again, only to hit the same exception again, and so forth. The logs showed that we hit the same SQLException about 300 times in a second!! ... this happened intermittently several times with occassional JVM pauses of 5 seconds or so, during which the application was not responsive, until eventually an Error was thrown and the thread died!
So we implemented a back-off strategy, approximately shown in the code below, that if the exception is not recoverable (or is excepted to recover within a matter of minutes), then we wait for a longer time before resuming operations.
class Test1 implements Runnable { public void run() { boolean backoff = false; while(true) { if (backoff) { Thread.sleep (TIME_FOR_LONGER_BREAK); backoff = false; } System.out.println("I'm test class 1"); try { // do important stuff here, use database and other critical resources } catch (SqlException se) { // code to delay the next loop backoff = true; } catch (Exception e) { } catch (Throwable t) { } } } }
If you implement your tasks this way then I don't see a point in having a third "watch-dog" thread with the checkTasks() method. Furthermore, for the same reasons I outlined above, I'd be cautious to just start the task again with the executor. First you need to understand why the task failed and whether the environment is in a stable condition that running the task again would be useful.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With