A threading feature within Java is the ExecutorService. This allows us to, in essence, create our own threadpools. This is useful because it allows us to create a limited number of threads that our code may run on, ensuring we do not end up with thread starvation or the number of threads getting out of hand.
By writing our code to use the ExecutorService we can also limit our methods to only use a single thread if we want.
Here’s a simple example of using the ExectorService
public static void main(String[] args) throws InterruptedException, ExecutionException { System.out.println("Main Thread - " + Thread.currentThread().getId()); //ExecutorService executorService = Executors.newSingleThreadExecutor(); ExecutorService executorService = Executors.newFixedThreadPool( Runtime.getRuntime().availableProcessors()); Future<?> f1 = executorService.submit(() -> outputInformation(1)); Future<?> f2 = executorService.submit(() -> outputInformation(2)); Future<?> f3 = executorService.submit(() -> outputInformation(3)); // the equivalent of waiting on the threads f1.get(); f2.get(); f3.get(); executorService.shutdown(); } private static void outputInformation(int id) { try { // some arbitrary time wasting Thread.sleep(2000); System.out.println(id + " - " + Thread.currentThread().getId()); }catch (Exception e) { } }
In the code above, we are creating our own threadpool (using Executors.newFixedThreadPool) with the size based upon the number of available processors. Hence in this example (assuming you have more than 1 processor on your machine) the resulting output is indeterminant, i.e. we might see 2 followed by 1, followed by 3 and all on different threads.
By simply changing to use Executors.newSingleThreadExecutor() the output will be in the correct order as only a single thread is used for each call.
The ExecutorService is a useful abstraction for containing and controlling multiple threads.