In the last post in this series, we understood what a Runnable is in Java. Some of you may have noticed a big limitation of the Runnable API, that it does not return a value. This means that while you can use a Runnable to execute some unit of work in a separate thread, there isn't a straightforward way for you to retrieve a meaningful value which may represent the result of that work.
For the reader's understanding, the code below shows one way how you might retrieve the result of a Runnable. However, please note that this is just for your understanding and you will not find code like this in production grade applications.
public static void main(String[] args) {
System.out.println("Main method has started.");
BlockingQueue<String> resultsHolder = new ArrayBlockingQueue<String>(10);
new Task(resultsHolder);
new Task(resultsHolder);
new Task(resultsHolder);
try {
System.out.println(resultsHolder.take());
System.out.println(resultsHolder.take());
System.out.println(resultsHolder.take());
} catch (InterruptedException e) {
// thrown when current thread was interrupted while waiting on the blocking call .take()
throw new RuntimeException(e);
}
System.out.println("Main method has finished.");
}
private static class Task extends Thread {
private static int numTasks;
private final int id;
private final BlockingQueue<String> resultsHolder;
public Task(BlockingQueue<String> resultsHolder) {
this.id = ++numTasks;
this.resultsHolder = resultsHolder;
this.start();
}
@Override
public void run() {
System.out.println("Task has started.");
try {
TimeUnit.MILLISECONDS.sleep(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
String result = "Hello, World! This is Task : " + id;
this.resultsHolder.add(result);
System.out.println("Task has finished.");
}
}
Java Concurrency Framework provides the Callable interface. Along with certain other differences and advantages (more about this in another post), It solves the above mentioned limitation of Runnable.
To execute a Callable task, you can use an ExecutorService to get a Future representing the result of the task. (More about Future and ExecutorService in another post)
public static void main(String[] args) {
System.out.println("Main method has started.");
Task t1 = new Task();
Task t2 = new Task();
Task t3 = new Task();
ExecutorService executorService = Executors.newFixedThreadPool(3);
Future<String> f1 = executorService.submit(t1);
Future<String> f2 = executorService.submit(t2);
Future<String> f3 = executorService.submit(t3);
try {
System.out.println(f1.get());
System.out.println(f2.get());
System.out.println(f3.get());
} catch (InterruptedException e) {
// thrown when current thread was interrupted
// while waiting on the blocking call .get()
throw new RuntimeException(e);
} catch (ExecutionException e) {
// thrown when attempting to retrieve the result
// of a task that aborted by throwing an exception.
throw new RuntimeException(e);
}
executorService.shutdown();
System.out.println("Main method has finished.");
}
private static class Task implements Callable<String> {
private static int numTasks;
private final int id;
public Task() {
this.id = ++numTasks;
}
@Override
public String call() {
System.out.println("Task has started.");
try {
TimeUnit.MILLISECONDS.sleep(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return "Hello, World! This is Task : " + id;
}
}
The output is
Main method has started.
Task has started.
Task has started.
Task has started.
Hello, World! This is Task : 1
Hello, World! This is Task : 2
Hello, World! This is Task : 3
Main method has finished.
In addition to Callables, An ExecutorService can also be used for executing Runnables.
The ExecutorService provides these methods for this purpose. Do check these out!
-
void execute(Runnable command)
[Java Doc Link] -
Future<?> submit(Runnable task)
[Java Doc Link]
Top comments (0)