Execute a continious task via ThreadPoolExecutor - java

Facing the problem with the ThreadPoolExecutor in Java.
How can I execute a continuous task using it? For example, I want to execute something like this:
#Async
void MyVoid(){
Globals.getInstance().increment();
System.out.println(Thread.currentThread().getName()+" iteration # "+ Globals.getInstance().Iterator);
}
I want it to run forever in 2 parallel asynchronous threads until the user sends a request to stop the ThreadPoolExecutor in the "/stop" controller.
If I use this for example:
#Controller
#RequestMapping("api/test")
public class SendController {
ThreadPoolExecutor executor = new ErrorReportingThreadPoolExecutor(5);
boolean IsRunning = true;
#RequestMapping(value = "/start_new", method = RequestMethod.POST)
public Callable<String> StartNewTask(#RequestBody LaunchSend sendobj) throws IOException, InterruptedException {
Runnable runnable = () -> { MyVoid();};
executor.setCorePoolSize(2);
executor.setMaximumPoolSize(2);
while (IsRunning) {
executor.execute(runnable);
System.out.println("Active threads: " + executor.getActiveCount());
}
return () -> "Callable result";
}
#RequestMapping(value = "/stop", method = RequestMethod.GET)
public Callable<String> StopTasks() {
executor.shutdown(); //for test
if(SecurityContextHolder.getContext().getAuthentication().getName() != null && SecurityContextHolder.getContext().getAuthentication().getName() != "anonymousUser") {
executor.shutdown();
return () -> "Callable result good";
}
else { return () -> "Callable result bad";}
}
}
public class ErrorReportingThreadPoolExecutor extends ThreadPoolExecutor {
public ErrorReportingThreadPoolExecutor(int nThreads) {
super(nThreads, nThreads,
0, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
#Override
protected void afterExecute(Runnable task, Throwable thrown) {
super.afterExecute(task, thrown);
if (thrown != null) {
// an unexpected exception happened inside ThreadPoolExecutor
thrown.printStackTrace();
}
if (task instanceof Future<?>) {
// try getting result
// if an exception happened in the job, it'll be thrown here
try {
Object result = ((Future<?>)task).get();
} catch (CancellationException e) {
// the job get canceled (may happen at any state)
e.printStackTrace();
} catch (ExecutionException e) {
// some uncaught exception happened during execution
e.printStackTrace();
} catch (InterruptedException e) {
// current thread is interrupted
// ignore, just re-throw
Thread.currentThread().interrupt();
}
}
}
}
I'm getting the following errors:
As I understood, a lot of tasks got submitted into the 'executor' queue within a few seconds and then the executor handled all them. (But I need each thread to wait before the current task ends and then submit the new one to the executor, I think.)
HTTP Requests to these controllers are forever "IDLE" until the next request comes, i.e. after sending a request to /api/test/start_new the controller's code executed tasks that are running, but the request is IDLE.
How can I do this in Java?
P.S. Spring MVC is used in the project. It has its own implementation of ThreadPoolExecutor - ThreadPoolTaskExecutor, but I am facing similar problems with it.

Related

What is the prod and cons of following implementation for waiting before execution when thread queue is full? [duplicate]

This question already has answers here:
ThreadPoolExecutor Block When its Queue Is Full?
(10 answers)
Closed 3 months ago.
We have a large text file in which each line requires intensive process. The design is to have a class that reads the file and delegates the processing of each line to a thread, via thread pool. The file reader class should be blocked from reading the next line once there is no free thread in the pool to do the processing. So i need a blocking thread pool
In the current implementation ThreadPoolExecutor.submit() and ThreadPoolExecutor.execute() methods throw RejectedExecutionException exception after the configured # of threads get busy as i showed in code snippet below.
public class BlockingTp {
public static void main(String[] args) {
BlockingQueue blockingQueue = new ArrayBlockingQueue(3);
ThreadPoolExecutor executorService=
new ThreadPoolExecutor(1, 3, 30, TimeUnit.SECONDS, blockingQueue);
int Jobs = 10;
System.out.println("Starting application with " + Jobs + " jobs");
for (int i = 1; i <= Jobs; i++)
try {
executorService.submit(new WorkerThread(i));
System.out.println("job added " + (i));
} catch (RejectedExecutionException e) {
System.err.println("RejectedExecutionException");
}
}
}
class WorkerThread implements Runnable {
int job;
public WorkerThread(int job) {
this.job = job;
}
public void run() {
try {
Thread.sleep(1000);
} catch (Exception excep) {
}
}
}
Output of above program is
Starting application to add 10 jobs
Added job #1
Added job #2
Added job #3
Added job #4
Added job #5
Added job #6
RejectedExecutionException
RejectedExecutionException
RejectedExecutionException
RejectedExecutionException
Can some one throw some light i.e how i can implement blocking thread pool.
Can some one throw some light i.e how i can implement blocking thread pool.
You need to set a rejection execution handler on your executor service. When the thread goes to put the job into the executor, it will block until there is space in the blocking queue.
BlockingQueue arrayBlockingQueue = new ArrayBlockingQueue(3);
ThreadPoolExecutor executorService =
new ThreadPoolExecutor(1, 3, 30, TimeUnit.SECONDS, arrayBlockingQueue);
// when the blocking queue is full, this tries to put into the queue which blocks
executorService.setRejectedExecutionHandler(new RejectedExecutionHandler() {
#Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
try {
// block until there's room
executor.getQueue().put(r);
// check afterwards and throw if pool shutdown
if (executor.isShutdown()) {
throw new RejectedExecutionException(
"Task " + r + " rejected from " + executor);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RejectedExecutionException("Producer interrupted", e);
}
}
});
So instead of the TRE throwing a RejectedExecutionException, it will call the rejection handler which will in turn try to put the job back on the queue. This blocks the caller.
Lets have a look at your code again:
for (int i = 1; i <= Jobs; i++)
try {
tpExe.submit(new WorkerThread(i));
System.out.println("job added " + (i));
} catch (RejectedExecutionException e) {
System.err.println("RejectedExecutionException");
}
So - when you try to submit, and the pool is busy, that exception is thrown. If you want to wrap around that, it could look like:
public void yourSubmit(Runnable whatever) {
boolean submitted = false;
while (! submitted ) {
try {
tpExe.submit(new WorkerThread(whatever));
submitted = true;
} catch (RejectedExecutionException re) {
// all threads busy ... so wait some time
Thread.sleep(1000);
}
In other words: use that exception as "marker" that submits are currently not possible.
You can use semaphore for to control the resource.Reader will read and create asynchronous task by acquiring semaphore.If every thread is busy the reader thread will wait till thread is available.
public class MyExecutor {
private final Executor exec;
private final Semaphore semaphore;
public BoundedExecutor(Executor exec, int bound) {
this.exec = exec;
this.semaphore = new Semaphore(bound);
}
public void submitTask(final Runnable command)
throws InterruptedException, RejectedExecutionException {
semaphore.acquire();
try {
exec.execute(new Runnable() {
public void run() {
try {
command.run();
} finally {
semaphore.release();
}
}
});
} catch (RejectedExecutionException e) {
semaphore.release();
throw e;
}
}
}
Here is a RejectedExecutionHandler that supports the desired behavior. Unlike other implementations, it does not interact with the queue directly so it should be compatible with all Executor implementations and will not deadlock.
import java.util.concurrent.Executor;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.function.BiFunction;
import static com.github.cowwoc.requirements.DefaultRequirements.assertThat;
import static com.github.cowwoc.requirements.DefaultRequirements.requireThat;
/**
* Applies a different rejection policy depending on the thread that requested execution.
*/
public final class ThreadDependantRejectionHandler implements RejectedExecutionHandler
{
private final ThreadLocal<Integer> numberOfRejections = ThreadLocal.withInitial(() -> 0);
private final BiFunction<Thread, Executor, Action> threadToAction;
/**
* #param threadToAction indicates what action a thread should take when execution is rejected
* #throws NullPointerException if {#code threadToAction} is null
*/
public ThreadDependantRejectionHandler(BiFunction<Thread, Executor, Action> threadToAction)
{
requireThat(threadToAction, "threadToAction").isNotNull();
this.threadToAction = threadToAction;
}
#SuppressWarnings("BusyWait")
#Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor)
{
if (executor.isShutdown())
return;
Thread currentThread = Thread.currentThread();
Action action = threadToAction.apply(currentThread, executor);
if (action == Action.RUN)
{
r.run();
return;
}
if (action == Action.REJECT)
{
throw new RejectedExecutionException("The thread pool queue is full and the current thread is not " +
"allowed to block or run the task");
}
assertThat(action, "action").isEqualTo(Action.BLOCK);
int numberOfRejections = this.numberOfRejections.get();
++numberOfRejections;
this.numberOfRejections.set(numberOfRejections);
if (numberOfRejections > 1)
return;
try
{
ThreadLocalRandom random = ThreadLocalRandom.current();
while (!executor.isShutdown())
{
try
{
Thread.sleep(random.nextInt(10, 1001));
}
catch (InterruptedException e)
{
throw new WrappingException(e);
}
executor.submit(r);
numberOfRejections = this.numberOfRejections.get();
if (numberOfRejections == 1)
{
// Task was accepted, or executor has shut down
return;
}
// Task was rejected, reset the counter and try again.
numberOfRejections = 1;
this.numberOfRejections.set(numberOfRejections);
}
throw new RejectedExecutionException("Task " + r + " rejected from " + executor + " because " +
"the executor has been shut down");
}
finally
{
this.numberOfRejections.set(0);
}
}
public enum Action
{
/**
* The thread should run the task directly instead of waiting for the executor.
*/
RUN,
/**
* The thread should block until the executor is ready to run the task.
*/
BLOCK,
/**
* The thread should reject execution of the task.
*/
REJECT
}
}
This works for me.
class handler implements RejectedExecutionHandler{
#Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
try {
executor.getQueue().put(r);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

How to implement blocking thread pool executor? [duplicate]

This question already has answers here:
ThreadPoolExecutor Block When its Queue Is Full?
(10 answers)
Closed 3 months ago.
We have a large text file in which each line requires intensive process. The design is to have a class that reads the file and delegates the processing of each line to a thread, via thread pool. The file reader class should be blocked from reading the next line once there is no free thread in the pool to do the processing. So i need a blocking thread pool
In the current implementation ThreadPoolExecutor.submit() and ThreadPoolExecutor.execute() methods throw RejectedExecutionException exception after the configured # of threads get busy as i showed in code snippet below.
public class BlockingTp {
public static void main(String[] args) {
BlockingQueue blockingQueue = new ArrayBlockingQueue(3);
ThreadPoolExecutor executorService=
new ThreadPoolExecutor(1, 3, 30, TimeUnit.SECONDS, blockingQueue);
int Jobs = 10;
System.out.println("Starting application with " + Jobs + " jobs");
for (int i = 1; i <= Jobs; i++)
try {
executorService.submit(new WorkerThread(i));
System.out.println("job added " + (i));
} catch (RejectedExecutionException e) {
System.err.println("RejectedExecutionException");
}
}
}
class WorkerThread implements Runnable {
int job;
public WorkerThread(int job) {
this.job = job;
}
public void run() {
try {
Thread.sleep(1000);
} catch (Exception excep) {
}
}
}
Output of above program is
Starting application to add 10 jobs
Added job #1
Added job #2
Added job #3
Added job #4
Added job #5
Added job #6
RejectedExecutionException
RejectedExecutionException
RejectedExecutionException
RejectedExecutionException
Can some one throw some light i.e how i can implement blocking thread pool.
Can some one throw some light i.e how i can implement blocking thread pool.
You need to set a rejection execution handler on your executor service. When the thread goes to put the job into the executor, it will block until there is space in the blocking queue.
BlockingQueue arrayBlockingQueue = new ArrayBlockingQueue(3);
ThreadPoolExecutor executorService =
new ThreadPoolExecutor(1, 3, 30, TimeUnit.SECONDS, arrayBlockingQueue);
// when the blocking queue is full, this tries to put into the queue which blocks
executorService.setRejectedExecutionHandler(new RejectedExecutionHandler() {
#Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
try {
// block until there's room
executor.getQueue().put(r);
// check afterwards and throw if pool shutdown
if (executor.isShutdown()) {
throw new RejectedExecutionException(
"Task " + r + " rejected from " + executor);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RejectedExecutionException("Producer interrupted", e);
}
}
});
So instead of the TRE throwing a RejectedExecutionException, it will call the rejection handler which will in turn try to put the job back on the queue. This blocks the caller.
Lets have a look at your code again:
for (int i = 1; i <= Jobs; i++)
try {
tpExe.submit(new WorkerThread(i));
System.out.println("job added " + (i));
} catch (RejectedExecutionException e) {
System.err.println("RejectedExecutionException");
}
So - when you try to submit, and the pool is busy, that exception is thrown. If you want to wrap around that, it could look like:
public void yourSubmit(Runnable whatever) {
boolean submitted = false;
while (! submitted ) {
try {
tpExe.submit(new WorkerThread(whatever));
submitted = true;
} catch (RejectedExecutionException re) {
// all threads busy ... so wait some time
Thread.sleep(1000);
}
In other words: use that exception as "marker" that submits are currently not possible.
You can use semaphore for to control the resource.Reader will read and create asynchronous task by acquiring semaphore.If every thread is busy the reader thread will wait till thread is available.
public class MyExecutor {
private final Executor exec;
private final Semaphore semaphore;
public BoundedExecutor(Executor exec, int bound) {
this.exec = exec;
this.semaphore = new Semaphore(bound);
}
public void submitTask(final Runnable command)
throws InterruptedException, RejectedExecutionException {
semaphore.acquire();
try {
exec.execute(new Runnable() {
public void run() {
try {
command.run();
} finally {
semaphore.release();
}
}
});
} catch (RejectedExecutionException e) {
semaphore.release();
throw e;
}
}
}
Here is a RejectedExecutionHandler that supports the desired behavior. Unlike other implementations, it does not interact with the queue directly so it should be compatible with all Executor implementations and will not deadlock.
import java.util.concurrent.Executor;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.function.BiFunction;
import static com.github.cowwoc.requirements.DefaultRequirements.assertThat;
import static com.github.cowwoc.requirements.DefaultRequirements.requireThat;
/**
* Applies a different rejection policy depending on the thread that requested execution.
*/
public final class ThreadDependantRejectionHandler implements RejectedExecutionHandler
{
private final ThreadLocal<Integer> numberOfRejections = ThreadLocal.withInitial(() -> 0);
private final BiFunction<Thread, Executor, Action> threadToAction;
/**
* #param threadToAction indicates what action a thread should take when execution is rejected
* #throws NullPointerException if {#code threadToAction} is null
*/
public ThreadDependantRejectionHandler(BiFunction<Thread, Executor, Action> threadToAction)
{
requireThat(threadToAction, "threadToAction").isNotNull();
this.threadToAction = threadToAction;
}
#SuppressWarnings("BusyWait")
#Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor)
{
if (executor.isShutdown())
return;
Thread currentThread = Thread.currentThread();
Action action = threadToAction.apply(currentThread, executor);
if (action == Action.RUN)
{
r.run();
return;
}
if (action == Action.REJECT)
{
throw new RejectedExecutionException("The thread pool queue is full and the current thread is not " +
"allowed to block or run the task");
}
assertThat(action, "action").isEqualTo(Action.BLOCK);
int numberOfRejections = this.numberOfRejections.get();
++numberOfRejections;
this.numberOfRejections.set(numberOfRejections);
if (numberOfRejections > 1)
return;
try
{
ThreadLocalRandom random = ThreadLocalRandom.current();
while (!executor.isShutdown())
{
try
{
Thread.sleep(random.nextInt(10, 1001));
}
catch (InterruptedException e)
{
throw new WrappingException(e);
}
executor.submit(r);
numberOfRejections = this.numberOfRejections.get();
if (numberOfRejections == 1)
{
// Task was accepted, or executor has shut down
return;
}
// Task was rejected, reset the counter and try again.
numberOfRejections = 1;
this.numberOfRejections.set(numberOfRejections);
}
throw new RejectedExecutionException("Task " + r + " rejected from " + executor + " because " +
"the executor has been shut down");
}
finally
{
this.numberOfRejections.set(0);
}
}
public enum Action
{
/**
* The thread should run the task directly instead of waiting for the executor.
*/
RUN,
/**
* The thread should block until the executor is ready to run the task.
*/
BLOCK,
/**
* The thread should reject execution of the task.
*/
REJECT
}
}
This works for me.
class handler implements RejectedExecutionHandler{
#Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
try {
executor.getQueue().put(r);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

Shutdown now on ExecutionException

I read a lot of post about ExecutorService, but I can't find the way of doing what I need.
I need some concurrent threads. When any of them throw a custom exception all the remaining tasks are canceled.
This is an example of what I did. The task are working concurrent, but aren't interrupted on exception.
public class Main {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(2);
List<Future> futures = new ArrayList<Future>();
futures.add(executorService.submit(new Callable<Void>() {
public Void call() throws Exception {
Thread.sleep(5000);
System.out.println("Task 1 done");
return null;
}
}));
futures.add(executorService.submit(new Callable<Void>() {
public Void call() throws Exception {
Thread.sleep(2000);
System.out.println("Task 2 done");
if (true) {
throw new CustomException("Error on task 2");
}
return null;
}
}));
executorService.shutdown();
try {
executeFutures(futures);
} catch (CustomException ex) {
System.out.println("Received:" + ex.getMessage());
executorService.shutdownNow();
}
}
private static void executeFutures(List<Future> futures) throws CustomException {
try {
for (Future f : futures) {
f.get();
}
} catch (ExecutionException | InterruptedException e) {
if (e.getCause() instanceof CustomException) {
throw (CustomException) e.getCause();
}
}
}
}
This is the output:
Task 2 done //exception is thrown here but task1 continue.
Task 1 done
Received:Error on task 2
Any help will be appreciated.
Your problem is due to the fact that the method executeFutures make the main thread call f.get() on the first Future instance corresponding to the long task, which makes it wait the duration of the task so at least 5 seconds whatever happens. Once done it will then call f.get() on the second Future which is already over so it gets immediately the CustomException from the ExecutionException and calls executorService.shutdownNow() but it is already too late as there is no more tasks left to interrupt.
What you could do, is to use a decorator of type Callable that will automatically shutdown the thread pool when a CustomException is thrown, this way the thread pool will be shutdown directly by the thread that has executed the task that throws the exception instead of using the main thread.
Something like this:
public class AutoShutdown<V> implements Callable<V> {
private final ExecutorService executorService;
private final Callable<V> task;
public AutoShutdown(final ExecutorService executorService, final Callable<V> task) {
this.executorService = executorService;
this.task = task;
}
#Override
public V call() throws Exception {
try {
return task.call();
} catch (CustomException e) {
executorService.shutdownNow();
throw e;
}
}
}
Then you will need to submit your tasks through the decorator as next:
futures.add(
executorService.submit(
new AutoShutdown<>(
executorService,
new Callable<Void>() {
public Void call() throws Exception {
Thread.sleep(5000);
System.out.println("Task 1 done");
return null;
}
}
)
)
);
futures.add(
executorService.submit(
new AutoShutdown<>(
executorService,
new Callable<Void>() {
public Void call() throws Exception {
Thread.sleep(2000);
System.out.println("Task 2 done");
if (true) {
throw new CustomException("Error on task 2");
}
return null;
}
}
)
)
);
Output:
Task 2 done
As you can see in the output, the task one has been interrupted soon enough.
The message "Received:Error on task 2" was not thrown, so it looks
like a successful execution, and is not the case
No it is only because the first call to f.get() throws an InterruptedException as expected which makes it exit from executeFutures because the catch is performed outside the loop, move it inside the loop as next:
private static void executeFutures(List<Future> futures) throws CustomException {
for (Future f : futures) {
try {
f.get();
} catch (ExecutionException | InterruptedException e) {
if (e.getCause() instanceof CustomException) {
throw (CustomException) e.getCause();
}
}
}
}
Output:
Task 2 done
Received:Error on task 2

Short lived ExecutorService for async processing

I have an method that can execute asynchronous request in fire and forget fashion.
Method is implemented as following :
private void publishWorkItem(final Object payload, final ZkWorkCompleteCallback callback)
{
if (payload == null)
throw new NullPointerException();
final ExecutorService executor = Executors.newSingleThreadExecutor(PUBLISH_WORK_THREAD_FACTORY);
try
{
executor.execute(() -> {
try
{
if (callback != null)
{
final ZkWorkItem retval = publishWorkItem(payload);
callback.onCompleted(retval);
}
}
catch (final InterruptedException e)
{
// suppressed
}
catch (final Exception e)
{
LOGGER.error("Unhandled exception", e);
if (callback != null)
callback.onError(e);
}
});
}
finally
{
executor.shutdown();
}
}
Issue is that I am creating new ExecutorService Executors.newSingleThreadExecutor for each async request instead of using fixed thread pool. Reason for that is that publishWorkItem(payload) method uses a CountDownLatch#await() which in turn will block the executing thread because is waits for Watcher to finish. This could quickly exhaust fixed size pool.
Simplified code of publishWorkItem(payload)
final CountDownLatch latch = new CountDownLatch(1);
zkClient.exists(pathToWatch, new Watcher()
{
#Override
public void process(final WatchedEvent event)
{
try
{
extractAndDelete(baos, event.getPath());
}
catch (final Exception e)
{
LOGGER.error("Unable to perform cleanup", e);
}
finally
{
latch.countDown();
}
}
}, true);
------ THIS IS THE PROBLEM (Blocks current thread) ------
latch.await();
So my question is: Are there better approaches to this type of problem.
I did profile the application and I don't see any performance issues, my concern was that it was creating large number of threads.
Why don't you use a ExecutorService.newCachedThreadPool()?
According to the javadoc, it suits your use-case
These pools will typically improve the performance of programs that execute many short-lived asynchronous tasks ... will reuse previously constructed threads if available
Instead of creating a new single thread pool on each call of publishWorkItem(), you create a cached thread pool once and use for all your queries. The number of threads is capped by Integer.MAX_VALUE, so you will not be limited like with fixed thread pool, but it should be creating less threads overall.

How can I terminate Tasks that have timed out in multithreading?

I need to make a library in which I will have synchronous and asynchronous methods in it.
executeSynchronous() - waits until I have a result, returns the result.
executeAsynchronous() - returns a Future immediately which can be processed after other things are done, if needed.
Core Logic of my Library
The customer will use our library and they will call it by passing DataKey builder object. We will then construct a URL by using that DataKey object and make a HTTP client call to that URL by executing it and after we get the response back as a JSON String, we will send that JSON String back to our customer as it is by creating DataResponse object. Some customer will call executeSynchronous() and some might call executeAsynchronous() so that's why I need to provide two method separately in my library.
Interface:
public interface Client {
// for synchronous
public DataResponse executeSynchronous(DataKey key);
// for asynchronous
public Future<DataResponse> executeAsynchronous(DataKey key);
}
And then I have my DataClient which implements the above Client interface:
public class DataClient implements Client {
private RestTemplate restTemplate = new RestTemplate();
private ExecutorService executor = Executors.newFixedThreadPool(10);
// for synchronous call
#Override
public DataResponse executeSynchronous(DataKey key) {
DataResponse dataResponse = null;
Future<DataResponse> future = null;
try {
future = executeAsynchronous(key);
dataResponse = future.get(key.getTimeout(), TimeUnit.MILLISECONDS);
} catch (TimeoutException ex) {
PotoLogging.logErrors(ex, DataErrorEnum.TIMEOUT_ON_CLIENT, key);
dataResponse = new DataResponse(null, DataErrorEnum.TIMEOUT_ON_CLIENT, DataStatusEnum.ERROR);
// does this look right the way I am doing it?
future.cancel(true); // terminating tasks that have timed out.
} catch (Exception ex) {
PotoLogging.logErrors(ex, DataErrorEnum.CLIENT_ERROR, key);
dataResponse = new DataResponse(null, DataErrorEnum.CLIENT_ERROR, DataStatusEnum.ERROR);
}
return dataResponse;
}
//for asynchronous call
#Override
public Future<DataResponse> executeAsynchronous(DataKey key) {
Future<DataResponse> future = null;
try {
Task task = new Task(key, restTemplate);
future = executor.submit(task);
} catch (Exception ex) {
PotoLogging.logErrors(ex, DataErrorEnum.CLIENT_ERROR, key);
}
return future;
}
}
Simple class which will perform the actual task:
public class Task implements Callable<DataResponse> {
private DataKey key;
private RestTemplate restTemplate;
public Task(DataKey key, RestTemplate restTemplate) {
this.key = key;
this.restTemplate = restTemplate;
}
#Override
public DataResponse call() {
DataResponse dataResponse = null;
String response = null;
try {
String url = createURL();
response = restTemplate.getForObject(url, String.class);
// it is a successful response
dataResponse = new DataResponse(response, DataErrorEnum.NONE, DataStatusEnum.SUCCESS);
} catch (RestClientException ex) {
PotoLogging.logErrors(ex, DataErrorEnum.SERVER_DOWN, key);
dataResponse = new DataResponse(null, DataErrorEnum.SERVER_DOWN, DataStatusEnum.ERROR);
} catch (Exception ex) {
PotoLogging.logErrors(ex, DataErrorEnum.CLIENT_ERROR, key);
dataResponse = new DataResponse(null, DataErrorEnum.CLIENT_ERROR, DataStatusEnum.ERROR);
}
return dataResponse;
}
// create a URL by using key object
private String createURL() {
String url = somecode;
return url;
}
}
Problem Statement:-
When I started working on this solution, I was not terminating the tasks that have timed out. I was reporting the timeout to the client, but the task continues to run in the thread pool (potentially occupying one of my limited 10 threads for a long time). So I did some research online and I found that I can cancel my tasks those have timed out by using cancel on future as shown below -
future.cancel(true);
But I wanted to make sure, does it look right the way I am doing in my executeSynchronous method to cancel the tasks that have got timedout?
Since I am calling cancel() on theFuture which will stop it from running if tasks is still in the queue so I am not sure what I am doing is right or not? What is the right approach to do this?
If there is any better way, then can anyone provide an example for that?
If task is still in the queue then cancelling it by simply calling future.cancel() is ok but obviously you don't know if that is in the queue. Also even if you ask future to interrupt the task it may not work as your task can still be doing something which is ignoring the thread interrupted status.
So you can use the future.cancel(true) but you need to make sure that your task (thread) does regard the thread interrupted status. For example as you mentioned you make http call, so you might need to close the http client resource as soon as thread is interrupted.
Please refer to the example below.
I have tried to implement the task cancellation scenario. Normally a thread can check isInterrupted() and try to terminate itself. But this becomes more complex when you are using thread pool executors, callable and if the task is not really like while(!Thread.isInterrupted()) {// execute task}.
In this example, a task is writing a file (I did not use http call to keep the it simple). A thread pool executor starts running the task but the caller wants to cancel it just after 100 milli seconds. Now future sends the interrupt signal to the thread but the callable task can not check it immediately while writing to file. So to make this happen callable maintains a list of IO resources it is going to use and as soon as future wants to cancel the task it just calls cancel() on all IO resources which terminates the task with IOException and then thread finishes.
public class CancellableTaskTest {
public static void main(String[] args) throws Exception {
CancellableThreadPoolExecutor threadPoolExecutor = new CancellableThreadPoolExecutor(0, 10, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
long startTime = System.currentTimeMillis();
Future<String> future = threadPoolExecutor.submit(new CancellableTask());
while (System.currentTimeMillis() - startTime < 100) {
Thread.sleep(10);
}
System.out.println("Trying to cancel task");
future.cancel(true);
}
}
class CancellableThreadPoolExecutor extends ThreadPoolExecutor {
public CancellableThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
}
#Override
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
return new CancellableFutureTask<T>(callable);
}
}
class CancellableFutureTask<V> extends FutureTask<V> {
private WeakReference<CancellableTask> weakReference;
public CancellableFutureTask(Callable<V> callable) {
super(callable);
if (callable instanceof CancellableTask) {
this.weakReference = new WeakReference<CancellableTask>((CancellableTask) callable);
}
}
public boolean cancel(boolean mayInterruptIfRunning) {
boolean result = super.cancel(mayInterruptIfRunning);
if (weakReference != null) {
CancellableTask task = weakReference.get();
if (task != null) {
try {
task.cancel();
} catch (Exception e) {
e.printStackTrace();
result = false;
}
}
}
return result;
}
}
class CancellableTask implements Callable<String> {
private volatile boolean cancelled;
private final Object lock = new Object();
private LinkedList<Object> cancellableResources = new LinkedList<Object>();
#Override
public String call() throws Exception {
if (!cancelled) {
System.out.println("Task started");
// write file
File file = File.createTempFile("testfile", ".txt");
BufferedWriter writer = new BufferedWriter(new FileWriter(file));
synchronized (lock) {
cancellableResources.add(writer);
}
try {
long lineCount = 0;
while (lineCount++ < 100000000) {
writer.write("This is a test text at line: " + lineCount);
writer.newLine();
}
System.out.println("Task completed");
} catch (Exception e) {
e.printStackTrace();
} finally {
writer.close();
file.delete();
synchronized (lock) {
cancellableResources.clear();
}
}
}
return "done";
}
public void cancel() throws Exception {
cancelled = true;
Thread.sleep(1000);
boolean success = false;
synchronized (lock) {
for (Object cancellableResource : cancellableResources) {
if (cancellableResource instanceof Closeable) {
((Closeable) cancellableResource).close();
success = true;
}
}
}
System.out.println("Task " + (success ? "cancelled" : "could not be cancelled. It might have completed or not started at all"));
}
}
For your REST Http client related requirement you can modify the factory class something like this -
public class CancellableSimpleClientHttpRequestFactory extends SimpleClientHttpRequestFactory {
private List<Object> cancellableResources;
public CancellableSimpleClientHttpRequestFactory() {
}
public CancellableSimpleClientHttpRequestFactory(List<Object> cancellableResources) {
this.cancellableResources = cancellableResources;
}
protected HttpURLConnection openConnection(URL url, Proxy proxy) throws IOException {
HttpURLConnection connection = super.openConnection(url, proxy);
if (cancellableResources != null) {
cancellableResources.add(connection);
}
return connection;
}
}
Here you need to use this factory while creating RestTemplate in your runnable task.
RestTemplate template = new RestTemplate(new CancellableSimpleClientHttpRequestFactory(this.cancellableResources));
Make sure that you pass the same list of cancellable resources that you have maintained in CancellableTask.
Now you need to modify the cancel() method in CancellableTask like this -
synchronized (lock) {
for (Object cancellableResource : cancellableResources) {
if (cancellableResource instanceof HttpURLConnection) {
((HttpURLConnection) cancellableResource).disconnect();
success = true;
}
}
}

Categories