If I have a button action that launches an AsyncTask. What if I click the button 10 times? Is the task executes 10 times and runs that much in background? Or is any previous task canceled and just the task reinitiated (that would be my desired behaviour)?
public void myButtonClick(View v) {
new MyAsyncTask().execute(params);
}
Each click on the button is producing a new instance of your MyAsyncTask, that means that each task is created and executed until its ends.
If you want to stop a previously launched task, you have to store a reference to the last launched task and cancel it like:
public void myButtonClick(View v) {
if (this.lastMyAsyncTask != null && this.lastMyAsyncTask.getStatus() != AsyncTask.Status.FINISHED){
this.lastMyAsyncTask.cancel();
}
this.lastMyAsyncTask = new MyAsyncTask();
this.lastMyAsyncTask.execute(params);
}
If you really want to keep the task to a single execution at a time, you should create a single threaded ThreadPool:
private ThreadPoolExecutor executor =
new ThreadPoolExecutor(1,1, 1, TimeUnit.SECONDS,
new SynchronousQueue(), new ThreadPoolExecutor.AbortPolicy());
private Future<Void> lastSubmitted;
private ReentrantLock submissionLock = new ReentrantLock();
...
try {
submissionLock.lock();
Future<Void> future =
executor.submit(new MyAsyncTask()); // assuming this implements Callable<Void>
lastSubmitted = future;
}
catch (RejectedExecutionException e) {
lastSubmitted.cancel();
Future<Void> future =
executor.submit(new MyAsyncTask()); // assuming this implements Callable<Void>
lastSubmitted = future;
}
finally {
submissionLock.unlock();
}
If you do it as suggested above, you'll get a RejectedExecutionException if you try to submit a task while one is already running. When that happens, you can cancel the last one submitted, and schedule a new execution. Keep in mind your task has to be interruptible (be careful with InputStream/OutputStream as they are not interruptible, use Channel objects instead).
The lock is needed so that you serialize the submission attempts. A thread could still get a RejectedExecutionException if another thread submitted a task during the catch block execution.
Related
We used multiple thread groups in projects for parallel execution like below
ThreadPoolExecutor executorService = (ThreadPoolExecutor) Executors.newFixedThreadPool(5);
Here my question is how to terminate other thread groups when exception comes in any one of the thread group.
thanks.
One option is to have a separate service which
tracks the relevant threadpools
tracks an exception flag
you delegate task submission to so it can wrap Runnables in a try-catch which sets the exception flag to true
periodically checks if the exception flag is true and, if so, attempts to shutdown all relevant threadpools
For example you could have something like below.
public class ThreadpoolService {
private final AtomicBoolean threadpoolException = new AtomicBoolean(false);
private final Set<ExecutorService> threadPools = new HashSet<>();
private final ScheduledExecutorService tracker = Executors.newSingleThreadScheduledExecutor();
public ThreadpoolService() {
// Start a thread tracking if an exception occurs in the threadpools, and if so attempts to shut them down
tracker.scheduleAtFixedRate(() -> {
if (threadpoolException.get()) {
shutdownThreadPools();
}
// Run the tracker every second, with an initial delay of 1 second before the first run
}, 1000, 1000, TimeUnit.MILLISECONDS);
}
private void shutdownThreadPools() {
// For each threadpool create a completable future which attempts to shut it down
final var threadpoolStopTasks = threadPools.stream()
.map(tp -> CompletableFuture.runAsync(() -> {
try {
tp.shutdown();
// Await termination, force if taking too long
if (!tp.awaitTermination(1000, TimeUnit.MILLISECONDS)) {
tp.shutdownNow();
}
} catch (InterruptedException e) {
tp.shutdownNow();
Thread.currentThread().interrupt();
}
}))
.collect(Collectors.toList());
// Create a completable future from all of the above stop tasks, wait for it to complete then
// stop the executor this tracker is running in
CompletableFuture.allOf(threadpoolStopTasks.toArray(new CompletableFuture[0]))
.thenApply((v) -> {
tracker.shutdownNow();
return null;
})
.join();
}
public void submit(ExecutorService threadPool, Runnable task) {
threadPools.add(threadPool);
threadPool.submit(() -> {
try {
task.run();
} catch (Exception e) {
// do stuff
threadpoolException.set(true);
}
});
}
public void shutdown() throws InterruptedException {
shutdownThreadPools();
tracker.shutdown();
if (!tracker.awaitTermination(1000, TimeUnit.MILLISECONDS)) {
tracker.shutdownNow();
}
}
}
Then in your program
final var threadpoolService = new ThreadpoolService();
// Then wherever you use a threadpool delegate to the above for task submissing
final var tp1 = Executors.newFixedThreadPool(5);
threadpoolService.submit(tp1, () -> {
// some task which may fail exceptionally
return;
});
When your program needs to shutdown for some other reason
threadpoolService.shutdown();
}
Of note is that an exception triggerring the shutdown of these threadpools is not recoverable i.e. the threadpools and ThreadpoolService are no longer in a functional state after shutdown and really, this should trigger the end of the program - you could enhance this to register a shutdown hook which ends the program.
It should also be noted that I've made a lot of assumptions inc.
use of the default fork-join pool for CompletableFutures (you can just pass your own executor service)
expectation the CompletableFuture.allOf will finish in a timely manner (you can add a timeout)
hardcoded time intervals (you can make these configurable)
It also doesn't cover the below, both of which can be resolved by using a guard (maybe threadpoolException) on appropriate methods and returning some value or throwing an exception as appropriate
race conditions on the various methods (e.g. calling shutdown while a shutdown is in progress)
calling submit following a shutdown
I have an ExecutorService that I submit tasks to:
private final ExecutorService CUSTOM_POOL = Executors
.newCachedThreadPool();
private Queue<Future<?>> customTasksHandles;
private boolean started;
private Runnable task = new Runnable() {
#Override
public void run() {
if (!started) {return;}
...
customTasksHandles.add(CUSTOM_POOL.submit(new CustomTask(customData)));
...
}
}
I need to create public void stop() and public void start() functions. The stop() function would future.cancel() for every task that has been submitted to the executor, while the start() would start running the task Runnable again.
public void stop() {
...
Future customTasksHandle= customTasksHandles.poll();
while (customTasksHandle!=null) {
customTasksHandle.cancel(true);
customTasksHandle=locationTasksHandles.poll();
}
...
started = false;
}
public void start() {started = true;}
I tried to just CUSTOM_POOL.shutdown(), however it seems to make it impossible to submit new tasks later, after the start() is called. It seems that the only way is to loop over all submitted tasks and call .cancel() on each.
However, how do I get all submitted tasks without adding each task to a list/queue when submitting? Is there a better way to do it other than the way above? I was hoping for a List<Future> submittedTasks = CUSTOM_POOL.getAllSubmittedTasks() method of sorts, but there doesn't seem to be one. Interestingly, .shutdown() and invoke() methods do return List<Future>, but have side-effects.
As you can see here you could use the shutdownNow() method to stop and retrieve all the task that where waiting for execution. If what you want is just stop ("pause") the procesing of the task and the continue with it, you migth want to keep track yourself of the status of the taks and when you pause and unapuse the task you can resubmit the task returned by the mehtod shutdownNow() and the one that where executing in the instant of the stop. You should take into account that to stop the threads the pool may call thread interrupt so, if you are executing some sensible work you should take care of it properly. There is no pause and unpause for threads. check this
You can achieve this by using Future, create start method which accepts Runnable and return Future
public Future<?> start(Runnable run) {
return CUSTOM_POOL.submit(run);
}
You can save all these Future in a List or Map so that you can cancel which ever you need by using custom stop method
public void stop(Future<?> future) {
future.cancel(true);
}
Example
public class TestMain {
private final ExecutorService CUSTOM_POOL = Executors
.newCachedThreadPool();
public static void main(String[] args) {
//custom logic
}
public Future<?> start(Runnable run) {
return CUSTOM_POOL.submit(run);
}
public void stop(Future<?> future) {
future.cancel(true);
}
}
Future
Attempts to cancel execution of this task. This attempt will fail if the task has already completed, has already been cancelled, or could not be cancelled for some other reason. If successful, and this task has not started when cancel is called, this task should never run. If the task has already started, then the mayInterruptIfRunning parameter determines whether the thread executing this task should be interrupted in an attempt to stop the task.
I have a javafx app, and I want to surround some code with "waiting" feature. So my code can be Runnable and Callable. The problem is getting result from Callabe. I tried to play with:
wait()/notify()
Platform.runLater
creating daemon threads by hands
Service
after reading some articles here, but it doesn't help.
How I want to call it:
final String a =
CommonHelper.showWaiting(() -> {
System.out.println("test");
return "test2";
});
That's how I work with Runnable:
public static void showWaiting(Runnable runnable) {
ExecutorService executorService = Executors.newFixedThreadPool(2);
try {
executorService.submit(new WaitingTask<>(executorService.submit(runnable)));
} finally {
executorService.shutdown();
}
}
And my WaitingTask is:
public class WaitingTask<T> extends Task<Void> {
#Getter
private final Future<T> future;
public WaitingTask(Future<T> future) {
this.future = future;
}
#Override
protected Void call() {
showSpinner();
while (true) {
if (future.isDone()) {
hideSpinner();
break;
}
}
}
return null;
}
}
That works awesome - my app shows waiting spinner, and task runns in separate thread.
So I try to work the same way with Callable to get the result:
public static <T> T showWaiting(Callable<T> callable) {
ExecutorService executorService = Executors.newFixedThreadPool(2);
try {
FutureTask<T> task = new FutureTask<>(callable);
Future<T> result = (Future<T>) executorService.submit(task);
executorService.submit(new WaitingTask<>(result));
return result.get();
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
executorService.shutdown();
}
}
but I can not see waiting spinner, maybe the app's main thread waits for result.get(); and the app freezes. How can I fix it?
There are a few things you are doing incorrectly:
You wrap your Callable in a FutureTask before submitting it to an ExecutorService. You don't need to do this, and in fact you shouldn't do this. Instead, just submit your Callable directly and you will get a Future in return.
Future<T> future = executor.submit(callable);
If you're using the core implementation of ExecutorService the returned Future will be a FutureTask anyway. Not that you should care—the only important thing is that its a Future. Note the same goes for Runnables; just submit them directly, don't wrap them in a FutureTask first.
You're submitting your Callable, getting a Future, and wrapping said Future in a Task...and then submitting your Task. This means you will have two tasks for every one you want to execute. Depending on how your ExecutorService is configured, this equates to using two threads per task.
You should be using your Task as if it was your Callable. Do the work inside the Task#call() method and return the result. Then only submit the Task, don't wrap it in anything first.
executor.execute(task); // Don't need the Future here, just use "execute"
If you want the result of the Task you can register callbacks (see this). The class is designed to invoke these callbacks on the JavaFX Application Thread.
task.setOnSucceeded(event -> {
T value = task.getValue();
// do something with value...
});
Note that Task extends FutureTask. This seems contradictory to point 1, but that's just how it is. Personally, I wouldn't have designed the class that way—it ends up wrapping the Task in another Future (likely FutureTask) when executed using the Executor Framework.
This is related to number 2; if you fix that issue then this issue inherently goes away.
You are spin waiting for the wrapped Future to complete. This is a waste of resources. The Future interface has a get() method that will block the calling thread until said Future is done. If the Future completes normally you'll get the value in return, else if it completes exceptionally an ExecutionException will be thrown. The third option is the calling thread is interrupted and an InterruptedException is thrown.
If the method names "showSpinner" and "hideSpinner" aren't misleading, you are updating the UI from a background thread. Never update the UI from a thread other than the JavaFX Application Thread. Now, you could wrap those calls in a Platform.runLater action, but you could also use the properties/callbacks of the Task. For instance, you could listen to the running property to know when to show and hide your spinner.
Taking all that into account, your example should look more like:
// Doesn't have to be an anonymous class
Task<String> task = new Task<>() {
#Override
protected String call() {
System.out.println("test");
return "test2";
}
});
task.runningProperty().addListener((obs, wasRunning, isRunning) -> {
if (isRunning) {
showSpinner();
} else {
hideSpinner();
}
});
task.setOnSucceeded(event -> {
String a = task.getValue();
// Do something with value.
});
executorService.execute(task);
For more information, I suggest reading:
Concurrency in JavaFX
Documentation of javafx.concurrent.Worker
Documentation of javafx.concurrent.Task (and Worker's other implementations)
Possibly a tutorial on Java's Executor Framework.
Thanks all for help, especially #Slaw and #kendavidson
Finally I've found a simple and perfect solution here:
Modal JaxaFX Progress Indicator running in Background
Maybe I'll post my full generic-based example here, based on this principles
In the system, I have an object - let's call it TaskProcessor. It holds queue of tasks, which are executed by some pool of threads (ExecutorService + PriorityBlockingQueue)
The result of each task is saved in the database under some unique identifier.
The user, who knows this unique identifier, may check the result of this task. The result could be in the database, but also the task could still wait in the queue for execution. In that case, UserThread should wait until the task will be finished.
Additionally, the following assumptions are valid:
Someone else could enqueue the task to TaskProcessor and some random UserThread can access the result if he knows the unique identifier.
UserThread and TaskProcess are in the same app. TaskProcessor contains a pool of threads, and UserThread is simply servlet Thread.
UserThread should be blocked when asking for the result, and the result is not completed yet. UserThread should be unblocked immediately after TaskProcessor complete task (or tasks) grouped by a unique identifier
My first attempt (the naive one), was to check the result in the loop and sleep for some time:
// UserThread
while(!checkResultIsInDatabase(uniqueIdentifier))
sleep(someTime)
But I don't like it. First of all, I am wasting database connections. Moreover, if the task would be finished right after sleep, then the user will wait even if the result just appeared.
Next attempt was based on wait/notify:
//UserThread
while (!checkResultIsInDatabase())
taskProcessor.wait()
//TaskProcessor
... some complicated calculations
this.notifyAll()
But I don't like it either. If more UserThreads will use TaskProcessor, then they will be wakened up unnecessarily every time some task would be completed and moreover - they will make unnecessary database calls.
The last attempt was based on something which I called waitingRoom:
//UserThread
Object mutex = new Object();
taskProcessor.addToWaitingRoom(uniqueIdentifier, mutex)
while (!checkResultIsInDatabase())
mutex.wait()
//TaskProcessor
... Some complicated calculations
if (uniqueIdentifierExistInWaitingRoom(taskUniqueIdentifier))
getMutexFromWaitingRoom(taskUniqueIdentifier).notify()
But it seems to be not secure. Between database check and wait(), the task could be completed (notify() wouldn't be effective because UserThread didn't invoke wait() yet), which may end up with deadlock.
It seems, that I should synchronize it somewhere. But I am afraid that it will be not effective.
Is there a way to correct any of my attempts, to make them secure and effective? Or maybe there is some other, better way to do this?
You seem to be looking for some sort of future / promise abstraction. Take a look at CompletableFuture, available since Java 8.
CompletableFuture<Void> future = CompletableFuture.runAsync(db::yourExpensiveOperation, executor);
// best approach: attach some callback to run when the future is complete, and handle any errors
future.thenRun(this::onSuccess)
.exceptionally(ex -> logger.error("err", ex));
// if you really need the current thread to block, waiting for the async result:
future.join(); // blocking! returns the result when complete or throws a CompletionException on error
You can also return a (meaningful) value from your async operation and pass the result to the callback. To make use of this, take a look at supplyAsync(), thenAccept(), thenApply(), whenComplete() and the like.
You can also combine multiple futures into one and a lot more.
I believe replacing of mutex with CountDownLatch in waitingRoom approach prevents deadlock.
CountDownLatch latch = new CountDownLatch(1)
taskProcessor.addToWaitingRoom(uniqueIdentifier, latch)
while (!checkResultIsInDatabase())
// consider timed version
latch.await()
//TaskProcessor
... Some complicated calculations
if (uniqueIdentifierExistInWaitingRoom(taskUniqueIdentifier))
getLatchFromWaitingRoom(taskUniqueIdentifier).countDown()
With CompletableFuture and a ConcurrentHashMap you can achieve it:
/* Server class, i.e. your TaskProcessor */
// Map of queued tasks (either pending or ongoing)
private static final ConcurrentHashMap<String, CompletableFuture<YourTaskResult>> tasks = new ConcurrentHashMap<>();
// Launch method. By default, CompletableFuture uses ForkJoinPool which implicitly enqueues tasks.
private CompletableFuture<YourTaskResult> launchTask(final String taskId) {
return tasks.computeIfAbsent(taskId, v -> CompletableFuture // return ongoing task if any, or launch a new one
.supplyAsync(() ->
doYourThing(taskId)) // get from DB or calculate or whatever
.whenCompleteAsync((integer, throwable) -> {
if (throwable != null) {
log.error("Failed task: {}", taskId, throwable);
}
tasks.remove(taskId);
})
);
/* Client class, i.e. your UserThread */
// Usage
YourTaskResult taskResult = taskProcessor.launchTask(taskId).get(); // block until we get a result
Any time a user asks for the result of a taskId, they will either:
enqueue a new task if they are the first to ask for this taskId; or
get the result of the ongoing task with id taskId, if someone else enqueued it first.
This is production code currently used by hundreds of users concurrently.
In our app, users ask for any given file, via a REST endpoint (every user on its own thread). Our taskIds are filenames, and our doYourThing(taskId) retrieves the file from the local filesystem or downloads it from an S3 bucket if it doesn't exist.
Obviously we don't want to download the same file more than once. With this solution I implemented, any number of users can ask for the same file at the same or different times, and the file will be downloaded exactly once. All users that asked for it while it was downloading will get it at the same time the moment it finishes downloading; all users that ask for it later, will get it instantly from the local filesystem.
Works like a charm.
What I understood from the question details is-
When UserThread requests for result, there are 3 possibilities:
Task has been already completed so no blocking of user thread and directly get result from DB.
Task is in queue or executing but not yet completed, so block the user thread(till now there should not be any db queries) and just after completion of task(the task result must be saved in DB at this point), unblock user thread(now user thread can query the DB for result)
There is no task submitted ever for the given uniqueIdentifier which user has requested, in this case there will be empty result from db.
For point 1 and 3, Its straight forward, there will not be any blocking of UserThread, just query the result from DB.
For point 2 - I have written a simple implementation of TaskProcessor. Here I have used ConcurrentHashMap to keep the current tasks which are not yet completed. This map contains the mapping between UniqueIdentifier and corresponding task. I have used computeIfPresent() (introduced in JAVA - 1.8) method of ConcurrentHashMap which guarantees that the invocation of this method is thread safe for the same key. Below is what java doc says:
Link
If the value for the specified key is present, attempts to compute a
new mapping given the key and its current mapped value. The entire
method invocation is performed atomically. Some attempted update
operations on this map by other threads may be blocked while
computation is in progress, so the computation should be short and
simple, and must not attempt to update any other mappings of this map.
So with use of this method, whenever there is a user thread request for a task T1 and if the task T1 is in queue or executing but not completed yet, then user thread will wait on that task.
When the task T1 will be completed, all the user requests thread which were waiting on task T1 will be notified and then we will remove task T1 from the above map.
Other classes reference used in below code are present on this link.
TaskProcessor.java:
import java.util.Map;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BiFunction;
public class TaskProcessor implements ITaskProcessor {
//This map will contain all the tasks which are in queue and not yet completed
//If there is scenario where there may be multiple tasks corresponding to same uniqueIdentifier, in that case below map can be modified accordingly to have the list of corresponding tasks which are not completed yet
private final Map<String, Task> taskInProgresssByUniqueIdentifierMap = new ConcurrentHashMap<>();
private final int QUEUE_SIZE = 100;
private final BlockingQueue<Task> taskQueue = new ArrayBlockingQueue<Task>(QUEUE_SIZE);
private final TaskRunner taskRunner = new TaskRunner();
private Executor executor;
private AtomicBoolean isStarted;
private final DBManager dbManager = new DBManager();
#Override
public void start() {
executor = Executors.newCachedThreadPool();
while(isStarted.get()) {
try {
Task task = taskQueue.take();
executeTaskInSeperateThread(task);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void executeTaskInSeperateThread(Task task) {
executor.execute(() -> {
taskRunner.execute(task, new ITaskProgressListener() {
#Override
public void onTaskCompletion(TaskResult taskResult) {
task.setCompleted(true);
//TODO: we can also propagate the taskResult to waiting users, Implement it if it is required.
notifyAllWaitingUsers(task);
}
#Override
public void onTaskFailure(Exception e) {
notifyAllWaitingUsers(task);
}
});
});
}
private void notifyAllWaitingUsers(Task task) {
taskInProgresssByUniqueIdentifierMap.computeIfPresent(task.getUniqueIdentifier(), new BiFunction<String, Task, Task>() {
#Override
public Task apply(String s, Task task) {
synchronized (task) {
task.notifyAll();
}
return null;
}
});
}
//User thread
#Override
public ITaskResult getTaskResult(String uniqueIdentifier) {
TaskResult result = null;
Task task = taskInProgresssByUniqueIdentifierMap.computeIfPresent(uniqueIdentifier, new BiFunction<String, Task, Task>() {
#Override
public Task apply(String s, Task task) {
synchronized (task) {
try {
//
task.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return task;
}
});
//If task is null, it means the task was not there in queue, so we direcltly query the db for the task result
if(task != null && !task.isCompleted()) {
return null; // Handle this condition gracefully, If task is not completed, it means there was some exception
}
ITaskResult taskResult = getResultFromDB(uniqueIdentifier); // At this point the result must be already saved in DB if the corresponding task has been processed ever.
return taskResult;
}
private ITaskResult getResultFromDB(String uniqueIdentifier) {
return dbManager.getTaskResult(uniqueIdentifier);
}
//Other thread
#Override
public void enqueueTask(Task task) {
if(isStarted.get()) {
taskInProgresssByUniqueIdentifierMap.putIfAbsent(task.getUniqueIdentifier(), task);
taskQueue.offer(task);
}
}
#Override
public void stop() {
isStarted.compareAndSet(true, false);
}
}
Let me know in comments if you have any queries.
Thanks.
I have a series of different "tasks" to be done using the same thread pool. I want to measure the time it takes to perform each task, but for that I need to wait for every task in the "task" (sorry for ambiguity) to finish.
When there's just one task I would normally do this:
ExecutorService e = Executors.newCachedThreadPool();
for (int i=0; i<100; ++i)
e.submit(target);
e.shutdown();
while (!e.isTerminated());
But since there will be several task submitted to the pool, I can't it down. All the methods that have something to do with waiting for the tasks to finish mention "after shutdown request". Well, what if I don't want to shut it down, but wait for all the threads to finish and then submit more tasks?
This is what I want to do:
ExecutorService e = Executors.newCachedThreadPool();
for (int i=0; i<100; ++i)
e.submit(target);
// wait for all targets to finish
for (int i=0; i<100; ++i)
e.submit(target); // submit different tasks
// wait... and so on
I thought of shutting the pool down and then "waking it up" again using prestartAllCoreThreads, but then I realized this was not an ExecutorService method but a ThreadPoolExecutor method. Could this be a solution? Shutting it down, waiting, and then activating the pool again? Seems a bit ugly to me.
I also thought that the most natural thing to do was to use a CyclicBarrier, but it seems too a specific way of doing this, while I think it would be the most logical thing to be able to use any ExecutorService for what I'm trying to do.
Is there any way I could stick to ExecutorServices and wait for all the tasks to finish?
Use CyclicBarrier for the work you need like so :
// the optionalRunnable can collect the data gathered by the tasks
CyclicBarrier b = new CyclicBarrier(numberOfTasks,optionalRunnable)
Task yourTaks = new Task(...., b);
// inside the run method call b.await() after the work is done;
executor.submit(yourTaks);
Optionally , you can also call await in the main thread and instantiate the barrier to numTasks + 1 . That way you are sure you're resubmitting tasks to the executor only after it's done processing the current batch
You can await the termination of that ExecutorService.
ExecutorService executor = Executors.newCachedThreadPool();
//do your stuff
try {
executor.shutdown();
executor.awaitTermination(5, TimeUnit.MINUTES);
} catch (InterruptedException e) {
//handle
}
Or use a CountDownLatch:
CountDownLatch latch = new CountDownLatch(totalNumberOfTasks);
ExecutorService taskExecutor = Executors.newFixedThreadPool(4);
while(...) {
taskExecutor.execute(new MyTask());
}
try {
latch.await();
} catch (InterruptedException E) {
// handle
}
and within your task (enclose in try / finally)
latch.countDown();
You could create a TaskListener interface which you pass into each task. Each task notifies the TaskListener when they start and stop. Then you can create a TimingTaskListener implementation which maintains a ConcurrentMap of the durations which can be queried later.
public interface TaskListener {
void onStart(String taskId);
void onEnd(String taskId);
}
public class Task implements Runnable {
private TaskListener taskListener;
private String taskId;
public Task(String taskId, TaskListener taskListener) {
this.taskId = taskId;
this.listener = listener;
}
public void run() {
listner.onStart(taskId);
try {
doStuff();
} finally {
listener.onEnd(taskId);
}
}
}
// TODO: Implement TimingTaskListener to save durations to a ConcurrentMap
TimingTaskListener timingListener = new TimingTaskListener();
Runnable task1 = new Task("task1", timingListener);
Runnable task2 = new Task("task2", timingListener);
Future<?> f1 = e.submit(task1);
Future<?> f2 = e.submit(task2);
// futures block until the task is finished.
// You could also use a CountDownLatch to achieve the same
f1.get();
f2.get();
long time1 = timingListener.getDuration("task1");
long time2 = timingListener.getDuration("task2");