mukund
mukund

Reputation: 2980

Is it possible to check time being taken by a method call in parallel

In a Spring Boot service class, let's say that I am making a method call processEvent().
The method processEvent() might be doing N number of things including making REST calls to other services.

How to check the time being taken by the method in parallel and if it crosses the threshold, then do something else e.g. throw exception ?

class EventService {  

    public void processEvent(ServiceContext context, Event event) {     

      // Field a time checker here for the below method.  
      processEvent(event);  
    }  
    public void processEvent(Event event) {  
      // this method does many things.  
    }  
}

Can this be achieved using CompletionService ? If yes, Please give an example!

EDIT:
The following code works but I have one query:

public void processEvent(ServiceContext context, Event event) {  

LOGGER.debug("Timestamp before submitting task = {}", System.currentTimeMillis());  

  Future<EventResponse> future = executor.submit(() -> {  
    LOGGER.debug("Timestamp before invoking = {}", System.currentTimeMillis());
    EventResponse eventResponse = processEvent(event);  
    LOGGER.debug("Timestamp after invoking = {}", System.currentTimeMillis());  
    return eventResponse;  
  });  

  try {
    LOGGER.debug("Thread sleep starts at = {}", System.currentTimeMillis());
    Thread.sleep(5000);
    LOGGER.debug("Thread sleep ended at = {}", System.currentTimeMillis());
  } catch (InterruptedException e) {
    LOGGER.debug("Going to print stack trace....");
    e.printStackTrace();
  }  

  if (!future.isDone()) {
    future.cancel(true);
    LOGGER.debug("task executor cancelled at = {}", System.currentTimeMillis());
  } else {
    EventResponse response = future.get();
    LOGGER.debug("Received Event ID = {}", response.getEventDetailsList().get(0).getEventID());
    return response;
  }  

  LOGGER.debug("Going to return error response at = {}", System.currentTimeMillis());  
  throw new Exception("Message");  
}

I am getting the below logs:

Timestamp before submitting task = 1579005638324
Thread sleep starts at = 1579005638326
Timestamp before invoking = 1579005638326
Thread sleep ended at = 1579005638526
task executor cancelled at = 1579005638527
Going to return error response at = 1579005638527
Timestamp after invoking = 1579005645228

How "Timestamp after invoking" is logged after "task executor cancelled at" ?

Upvotes: 3

Views: 271

Answers (2)

John Vint
John Vint

Reputation: 40256

You can use a mix of a standard ThreadPoolExecutor with a ScheduledThreadPoolExecutor. The latter will cancel the submission of the former if it's still running.

ThreadPoolExecutor executor = ...;

ScheduledThreadPoolExecutor watcher = ...;

Future<?> future = executor.submit(() -> { ...  })

watcher.schedule(() -> future.cancel(true), THRESHOLD_SECONDS, TimeUnit.SECONDS);

The future.cancel(true) will be a no-op if it completed. For this though, you should be aware of how to handle cross-thread communiccation and cancellation. cancel(true) says "Either prevent this from running entirely, or, if it is running, interrupt the thread indicating we need to stop execution entirely and immediately"

From there your Runnable should handle interruption as a stop condition:

executor.submit(()-> {
   // do something
   if(Thread.currentThread().isInterrupted()) {
     // clean up and exit early
   }
   // continue doing something
});

Upvotes: 1

Forketyfork
Forketyfork

Reputation: 7810

You can use ThreadPoolTaskExecutor to submit the task, then sleep for a certain amount of time, then check if the task is completed and interrupt it, if it's still working. However, you can't just kill the task, you'll have to periodically check for the interrupted flag inside the task itself. The code would be something like:

@Autowired
private ThreadPoolTaskExecutor executor;

// ...

Future<?> future = executor.submit(() -> {

  doOneThing();

  if(Thread.interrupted()) {
    return;
  }

  doAnotherThing();

  if(Thread.interrupted()) {
    return;
  }

  // etc.
});

Thread.sleep(10000);

if (!future.isDone()) {
  future.cancel(true);
}

Upvotes: 1

Related Questions