Explain Codes LogoExplain Codes Logo

Choose between ExecutorService's submit and ExecutorService's execute

java
exception-handling
thread-pool-executor
callable
Anton ShumikhinbyAnton Shumikhin·Sep 25, 2024
TLDR

When you need a Future tracker or a result retriever, go for submit. If your task is just about running without any result management, opt for execute.

  • submit: Not just fire and forget, you get a Future to access the result or cancel the task.

    ExecutorService executor = Executors.newSingleThreadExecutor(); Future<String> future = executor.submit(() -> "result"); // "result" is a boring string. Try "404 Constraint Not Found" :)
  • execute: Just fire and forget. Run tasks and there's no result management conundrum.

    ExecutorService executor = Executors.newSingleThreadExecutor(); executor.execute(() -> System.out.println("Task accomplished!")); // Be it saving a princess or washing my cat. All in a day's work!

submit for versatility; execute for straightforwardness.

Understanding exception handling

execute uses the thread's UncaughtExceptionHandler to catch those notorious uncaught exceptions. That's not something you get with submit.

With submit, exceptions get trapped inside the Future and are blabbered out as ExecutionException when trying to get the task result:

try { Future<?> future = executor.submit(() -> { throw new RuntimeException("Oops! Butterfingers!"); }); // Handling a RuntimeException as smooth as jazz. future.get(); } catch (ExecutionException ee) { Throwable originalCause = ee.getCause(); // Digging through the ExecutionException to find the real culprit! }

Picking suitable tasks

A quick refresher: execute only accepts Runnable, but submit plays the big card, accepting both Runnable and Callable. A Callable is a cool friend when your task returns a value or throws an exception worthy of some attention:

Callable<Integer> task = () -> 1 + 1; // Math. The only place where "1 + 1" doesn't make you look stupid. :D Future<Integer> future = executor.submit(task);

Making informed choices

When the return value is like a bad joke and you need a simple procedure for task execution, execute might earn your vote. It's as easy as eating nachos from a hat for runs-and-forget scenarios.

In contrast, submit comes in handy when you have complicated needs:

  • Track the task progress and possibly cancel that runaway train.
  • Wait for the task completion without shutting down the executor.
  • Handle exceptions as though you are the Sherlock Holmes of code using Future#get.

Taking control through overriding

Ever wanted better control? Try creating a CustomThreadPoolExecutor or an ExtendedExecutor and overriding the afterExecute:

public class CustomThreadPoolExecutor extends ThreadPoolExecutor { @Override protected void afterExecute(Runnable r, Throwable t) { super.afterExecute(r, t); if (t == null && r instanceof Future<?>) { try { Future<?> future = (Future<?>) r; if (future.isDone()) { future.get(); } } catch (CancellationException ce) { t = ce; } catch (ExecutionException ee) { t = ee.getCause(); // Easter Egg hunt for the real exception } catch (InterruptedException ie) { Thread.currentThread().interrupt(); // "Stop poking me! - Thread, probably." } } if (t != null) { // You know the drill. Hook, line & sinker. } } }

Practical use cases

Running tasks without the traffic jam

Sometimes you need a fast lane for your tasks. Using Executors utility methods or a wrapped Callable can ensure your submit is not stuck in traffic:

executor.submit(new NonBlockingTaskWrapper<>(() -> { /* Task's secret mission */ }));

User request handling

An ExecutorService becomes your toolkit for dynamic task management, a great help for services handling user requests or processing data streams. The verdict between submit and execute depends on how much screen time you want for the task outputs.

Streamlining error logging

ExtendedExecutor with overridden methods can tidy up your error handling, making sure your error logs look as sharp as Sherlock Holmes on a good day. Gain control over log markings for misbehaving tasks.