Debapriyo Majumder :
Requirements
I need to be able to trigger a (long running) job via a POST call and return immediately.
Only one thread can run the job at one time.
The job being an expensive one, I want all future triggers of this job to not do anything if one job is already in progress.
Code
@RestController
public class SomeTask {
private SomeService someService;
@Autowired
public SomeTask(SomeService someService) {
this.someService = someService;
}
@Async // requirement 1
@RequestMapping(method = RequestMethod.POST, path = "/triggerJob")
public void triggerJob() {
expensiveLongRunningJob();
}
/**
* Synchronized in order to restrict multiple invocations. // requirement 2
*
*/
private synchronized void expensiveLongRunningJob() {
someService.executedJob();
}
}
Question
With the above code requirements 1 and 2 are satisfied. What is the best way to satisfy requirement 3 as well (have the new thread, created as a result of a POST call, skip the synchronised method and return immediately on failure to acquire a lock)?
Matt Timmermans :
Synchronization isn't the right tool for the job. You can do it like this:
@RestController
public class SomeTask {
private SomeService someService;
private final AtomicBoolean isTriggered = new AtomicBoolean();
@Autowired
public SomeTask(SomeService someService) {
this.someService = someService;
}
@Async // requirement 1
@RequestMapping(method = RequestMethod.POST, path = "/triggerJob")
public void triggerJob() {
if (!isTriggered.getAndSet(true)) {
try {
expensiveLongRunningJob();
} finally {
isTriggered.set(false);
}
}
}
/**
* only runs once at a time, in the thread that sets isTriggered to true
*/
private void expensiveLongRunningJob() {
someService.executedJob();
}
}