Site Search:

Create worker threads using Runnable, Callable and use an ExecutorService to concurrently execute tasks

Back>

Java Concurrency API use Executors to manage threads:
java concurrency API
java concurrency API

Executors

Interfaces. Executor is a simple standardized interface for defining custom thread-like subsystems, including thread pools, asynchronous I/O, and lightweight task frameworks. Depending on which concrete Executor class is being used, tasks may execute in a newly created thread, an existing task-execution thread, or the thread calling execute, and may execute sequentially or concurrently. ExecutorService provides a more complete asynchronous task execution framework. An ExecutorService manages queuing and scheduling of tasks, and allows controlled shutdown. The ScheduledExecutorService subinterface and associated interfaces add support for delayed and periodic task execution. ExecutorServices provide methods arranging asynchronous execution of any function expressed as Callable, the result-bearing analog of Runnable. A Future returns the results of a function, allows determination of whether execution has completed, and provides a means to cancel execution.


Timing

The TimeUnit class provides multiple granularities (including nanoseconds) for specifying and controlling time-out based operations. Most classes in the package contain operations based on time-outs in addition to indefinite waits.



In java 8, both Runnable and Callable are functional interfaces.

@FunctionalInterface public interface Runnable {
    void run();
}

@FunctionalInterface public interface Callable<V> {
    V call() throws Exception;
}

You can run a task in a new thread like the following code:
(new Thread(() -> System.out.println("I am a runnable."))).start();

While Runnable returns void, Callable returns a value with type V and throws checked exception.

OCPJP can have tricky questions on ExecutorService interface with these subtle difference.

ExecutorService methods execute, submit all execute tasks at some point in the future.

void execute(Runnable r);
Future<?> submit(Runnable task);
<T> Future<T> submit(Callable<T> task);

OCPJP>cat CallableVsRunnable.java 
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class CallableVsRunnable {
    
    public static void main(String... args) {
        ExecutorService exec = Executors.newSingleThreadExecutor();
        
        System.out.println("Active, exec.isShutdown(): " + exec.isShutdown());
        System.out.println("Active, exec.isTerminated(): " + exec.isTerminated());
        
        exec.submit(() -> System.out.println("I am runnable"));
        exec.execute(() -> System.out.println("I still am runnable"));
        
        exec.submit(() -> {System.out.println("I am a runnable, because I return void"); return;});
        exec.execute(() -> {System.out.println("I am still a runnalbe, because I return void"); return;});
        
        exec.submit(() -> {throw new RuntimeException("I am a runnable, because I don't return");} );
        exec.execute(() -> {throw new RuntimeException("I am still a runnable, because I don't return");} );
        
        exec.submit(() -> {throw new Exception("I am callable, because I throw Checked exception");});
        //won't compile
        //exec.execute(() -> {throw new Exception("I am callable, because I throw Checked exception");});
        
        exec.submit(() -> null);
        //won' compile
        //exec.execute(() -> null);
        
        System.out.println("==================");
        Future<String> f = exec.submit(() -> "I am callable, because I return this string.");
        System.out.println("f.isDone(): " + f.isDone());
        System.out.println("f.isCancelled(): " + f.isCancelled());
        try {
            System.out.println("f.get(1000, TimeUnit.MICROSECONDS): " + f.get(1000, TimeUnit.MICROSECONDS));
        } catch (TimeoutException e) {
            e.printStackTrace();
        } catch (InterruptedException e1) {
            e1.printStackTrace();
        } catch (ExecutionException e1) {
            e1.printStackTrace();
        }
        System.out.println("f.cancel(false): " + f.cancel(false));
        try {
            System.out.println("f.get(): " + f.get());
        } catch (InterruptedException e1) {
            e1.printStackTrace();
        } catch (ExecutionException e1) {
            e1.printStackTrace();
        }
        System.out.println("==================");
        
        //won't compile
        //exec.execute(() -> "I am callable, because I return this string.");
        
        exec.submit(() -> {
            System.out.println("I am a callable, because I return. I don't have to handle Checked Exception, because Callable declares it");
            Thread.sleep(1000);
            return null;
        });
        //won't compile
        //exec.execute(() -> {System.out.println("I am a callable, because I return");Thread.sleep(1000);return null;});
        //won't compile
        //exec.submit(() -> {System.out.println("I am a runnable, because I don't return."); Thread.sleep(1000);});
        exec.submit(() -> {
            System.out.println("I am a runnable, because I don't return. I have to handle Checked Exceptions");
            try{
                Thread.sleep(1000);
            } catch(InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });
        
        exec.shutdown();
        System.out.println("Shutting Down, exec.isShutdown(): " + exec.isShutdown());
        System.out.println("Shutting Down, exec.isTerminated(): " + exec.isTerminated());
        try {
            exec.awaitTermination(10, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        
        System.out.println("Shutdown, exec.isShutdown(): " + exec.isShutdown());
        System.out.println("Shutdown, exec.isTerminated(): " + exec.isTerminated());
    }
}
OCPJP>javac CallableVsRunnable.java 
OCPJP>java CallableVsRunnable
Active, exec.isShutdown(): false
Active, exec.isTerminated(): false
I am runnable
I still am runnable
I am a runnable, because I return void
I am still a runnalbe, because I return void
Exception in thread "pool-1-thread-1" ==================
java.lang.RuntimeException: I am still a runnable, because I don't return
at CallableVsRunnable.lambda$main$5(CallableVsRunnable.java:23)
at CallableVsRunnable$$Lambda$6/713338599.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
f.isDone(): true
f.isCancelled(): false
f.get(1000, TimeUnit.MICROSECONDS): I am callable, because I return this string.
f.cancel(false): false
f.get(): I am callable, because I return this string.
==================
I am a callable, because I return. I don't have to handle Checked Exception, because Callable declares it
Shutting Down, exec.isShutdown(): true
Shutting Down, exec.isTerminated(): false
I am a runnable, because I don't return. I have to handle Checked Exceptions
Shutdown, exec.isShutdown(): true
Shutdown, exec.isTerminated(): true
OCPJP>



Other TimeUnit includes: TimeUnit.NANOSECONDS, MICROSECONDS, MILLISECONDS, SECONDS, MINUTES, HOURS, DAYS.