I have a couple controller methods that must not be allowed to run at the same time:
#Scheduled(cron = "0 * * * * *")
public void first() {
// Acts on database
}
#RequestMapping(value = "/second", method = RequestMethod.POST)
public void second() {
// Also acts on same database
}
First one runs only as a scheduled job. Second one only runs as an HTTP request.
When second() gets called I want it to wait until first() finishes if it is running, then run immediately afterwards. If first() is not running, want second() to run and block first() from running while second() is still running. By block I mean don't allow first() to run at all, don't wait for second() to finish or queue it to run later either. It will attempt to run again only on its next scheduled run.
Edit:
If second() gets requested again while the previous request to second() has not yet completed, want that new request to be ignored.
If you have to maintain this only on one instance of your application, then you can use for example AtomicBoolean:
Let's create additional method, where you make something like this:
private AtomicBoolean isSecondRunning = new AtomicBoolean();
#Scheduled(cron = "0 * * * * *")
public void first() {
if (isSecondRunning.get()) {
return; // 1
}
execute();
}
#RequestMapping(value = "/second", method = RequestMethod.POST)
public void second() {
isSecondRunning.set(true); // 2
try {
execute();
} finally {
isRunning.set(false); // 3
}
}
public synchronized void execute(){
// here execute the code
}
Code explanation:
if isSecondRunning is true, then return from first without execution, if is false, then skip if and go to execute() method
when second is executed, then set isSecondRunning to true and then execute
set isSecondRunning to false, and do it inside finally block, so we can be sure, that it is set to false even if some exception occurs in your execution
The execute method is synchronized, so if first is running, then second will wait
The easiest way would to make both call a method in another layer (e.g. a service). That method, if declared on a singleton bean, can be synchronized so only one thread will be able to execute it at the same time in the same server.
class ScheduledTasks{
#Autowired private Service service;
#Scheduled(cron = "0 * * * * *")
public void first() {
service.doStuff();
}
}
class MyController{
#Autowired private Service service;
#RequestMapping(value = "/second", method = RequestMethod.POST)
public void second() {
service.doStuff();
}
}
#Service
class Service{
public synchronized void doStuff(){...}
}
Be aware, though, that it will cause concurrent requests to your endpoint to seemingly "halt" until the previous ones have completed, when they attempt to call that method.
As an alternative, you may want to convert your Scheduled method to a Quartz job and modify the trigger when your controller is called. This would also require some degree of synchronization so the triggers are modified atomically among concurrent requests, and also you may still need a synchronized method to guarantee that if first() is already running you don't execute the changes from second().
The problem is you can't really do anything simple because the outcome of whether a job is allowed to be queued or not depends on what jobs are already in the queue.
I would suggest you need a JobManager that controls the queue, and a JobRunner that takes any jobs from the queue and runs them. You need to both check the contents of the queue and add to the queue under the same exclusive lock.
public class JobManager {
private final Queue<Jobs> queue;
private final JobRunner jobRunner;
public JobManager() {
this.queue = new LinkedList<Jobs>();
this.jobRunner = new JobRunner(this);
jobRunner.start();
}
public synchronized void requestFirst() {
if (queue.isEmpty()) {
queue.add(Jobs.FIRST);
notifyAll();
}
}
public synchronized void requestSecond() {
if (!queue.contains(Jobs.SECOND)) {
queue.add(Jobs.SECOND);
notifyAll();
}
}
public synchronized Jobs getJob() throws InterruptedException {
while (queue.isEmpty()) {
wait();
}
return queue.peek();
}
public synchronized void notifyFinished(Jobs job) {
queue.remove(job);
}
public void startRunner() {
jobRunner.start();
}
public void stopRunner() {
jobRunner.stopRunner();
}
}
public class JobRunner extends Thread {
private final JobManager jobManager;
private volatile boolean stopping;
public JobRunner(JobManager jobManager) {
this.jobManager = jobManager;
this.stopping = false;
}
public void stopRunner() {
stopping = true;
this.interrupt();
}
#Override
public void run() {
while (!stopping) {
try {
Jobs job = jobManager.getJob();
if (job.equals(Jobs.FIRST)) {
// run first job
} else if (job.equals(Jobs.SECOND)) {
// run second job
}
jobManager.notifyFinished(job);
} catch (InterruptedException ignored) {}
}
}
}
public enum Jobs {
FIRST,
SECOND
}
#Controller
public class WebAccess {
private final JobManager jobManager;
public WebAccess() {
jobManager = new JobManager();
}
#Scheduled(cron = "0 * * * * *")
public void first() {
jobManager.requestFirst();
}
#RequestMapping(value = "/second", method = RequestMethod.POST)
public void second() {
jobManager.requestSecond();
}
#EventListener(ContextClosedEvent.class)
public void stopRunner() {
jobManager.stopRunner();
}
}
Unfortunately because of your complicated requirements for choosing first or second job, you need to hold a synchronization lock whilst checking the queue and deciding whether to add the job or not.
This kind of approach may be extremely difficult to test so probably it should be a last resort.
If you want to get Spring to do the autowiring you could annotate it accordingly.
Related
I have two APIs: one starts the thread, and another stops the thread. I'm successfully able to start a thread by calling /start API, but I'm unable to stop already running thread by calling /stop API. Seems like Executor#stop() does nothing.
My RestController:
#Autowired
private Executor executor;
#RequestMapping(path = "/start", method = GET)
public ResponseEntity<HttpStatus> startLongTask() {
executor.start();
return ResponseEntity.ok(HttpStatus.OK);
}
#RequestMapping(path = "/stop", method = GET)
public ResponseEntity<HttpStatus> stopLongTask() {
executor.stop();
return ResponseEntity.ok(HttpStatus.OK);
}
My Executor:
#Component
public class Executor {
#Value("${threads.number}")
private int threadsNumber;
private ExecutorService executorService;
#Autowired
private OtherService otherService;
#PostConstruct
private void init() {
executorService = Executors.newFixedThreadPool(threadsNumber);
executorService = Executors.newScheduledThreadPool(threadsNumber);
}
/**
* Start.
*/
public void start() {
executorService.submit(() -> otherService.methodImExecuting());
}
/**
* Stop.
*/
#PreDestroy
publicvoid stop() {
executorService.shutdownNow();
try {
if (!executorService.awaitTermination(800, TimeUnit.MILLISECONDS)) {
executorService.shutdownNow();
}
} catch (InterruptedException e) {
executorService.shutdownNow();
}
}
}
Here's the methodImExecuting:
#Component
public class OtherService {
public void methodImExecuting() {
List<SomeObject> dataList = repository.getDataThatNeedsToBeFilled();
for (SomeObject someObject : dataList) {
gatewayService.sendDataToOtherResourceViaHttp(someObject);
}
}
}
Short answer: You can not stop a running thread which does not cooperate. There's a deprecated destroy() method for threads, but this will lead to a "bad" state of your VM.
The only possibility to end the Thread clean is to interrupt it. But to check for interruption is the task of the thread itself.
So your methodImExcecuting sould look like:
void methodImExecuting() throws InterruptedException {
// it depends on your implementation, I assume here that you iterate
// over a collection for example
int loopCount = 0;
for (Foo foo : foos) {
++loopCount;
if (loopCount % 100 == 0) {
if (Thread.interrupted())
throw new InterruptedException();
}
...
}
It depends on your implementation how often you have to look if your thread was interrupted. But it's a fact that the call of executorService.shutdownNow(); will only set the interrupted flag of all threads currently running in the executorService. To really interrupt the thread, the thread must itself check if the interrupted flag is set and then throw an InterruptedException
Your running threads have to react to the interrupt signal
Thread.currentThread().isInterrupted()
Otherwise the sending of the interrupt signal has no effect.
Here you can find a good explanation:
Difference between shutdown and shutdownNow of Executor Service
Here I've got a thread
#Component("sessionStateListenerThread")
public class SessionStateListenerThread extends Thread {
private static final String DEFAULT_RESULT = "expired";
private List<DeferredResult<String>> subscribed = Collections
.synchronizedList(new ArrayList<DeferredResult<String>>());
/**
* Main thread loop for checking session state.
*/
#Override
public void run() {
while (true) {
synchronized (subscribed) {
try {
if (null == SecurityHelper.getUserLogin()) {
Iterator<DeferredResult<String>> it = subscribed.iterator();
while (it.hasNext()) {
DeferredResult<String> result = it.next();
result.setResult(DEFAULT_RESULT);
it.remove();
}
}
} catch (NullPointerException e) {
}
}
}
}
/**
* Adds client to listen session state.
*
* #param client
*/
public void addClient(DeferredResult<String> client) {
synchronized (subscribed) {
subscribed.add(client);
}
};
}
This class is used to wrap that thread
#Component
#DependsOn("sessionStateListenerThread")
public class SessionStateListener {
#Autowired
private SessionStateListenerThread thread;
#PostConstruct
public void configThread() {
thread.setDaemon(true);
thread.setName("SessionStateChecker");
}
/**
* Starts listener.
*/
public void startListener() {
thread.start();
}
/**
* Adds client to write result.
*
* #param client object to write result
*/
public void addSessionStateListener(DeferredResult<String> client) {
thread.addClient(client);
}
}
I put method startListener inside controller method. Also in controller I access method getUserLogin.
String login = SecurityHelper.getUserLogin(); // successfully access it
listener.startListener();
Method run in thread want to access this method getUserLogin too.
But despite login value being not null - invokation of SecurityHelper.getUserLogin() throws NullPointerException inside thread.
What's the problem? getUserLogin looks like this
return SecurityContextHolder.getContext().getAuthentication().getName();
Stacktrace is somehow small (e.printstacktrace())
java.lang.NullPointerException
at com.ui.web.security.SecurityHelper.getUserLogin(SecurityHelper.java:45)
at com.ui.web.controller.rest.poll.SessionStateListenerThread.run(SessionState
ListenerThread.java:41)
It's because you can't get current session data (context) into separate thread than current request thread.
You will be able to get any session data into any controller method, because any controller method will be executed within request thread.
I'm implementing a layer to wrap a 3rd party communication layer.
The contract I need to implement is:
FutureTask<SomeData> send(Request request);
My layer has an onMessageReceived method, which is called by the 3rd party when a response arrives.
The approach I've taken to implement my layer is as follows:
I have a callable, which waits on a condition with a timeout:
interface MyCallable<T> extends Callable<T> {
void signal();
}
class CallableWithSignal<T> implements MyCallable<T> {
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
private long waitTime;
public CallableWithSignal(int waitTimeSeconds){
this.waitTime=waitTimeSeconds;
}
#Override
public T call() throws Exception {
lock.lock();
try {
boolean wasSignaled = condition.await(waitTime, TimeUnit.SECONDS);
if(wasSignaled)
return null;
System.out.println("throwing exeption");
throw new Exception("timeout");
} finally {
lock.unlock();
}
}
#Override
public void signal() {
lock.lock();
try {
condition.signal();
} finally {
lock.unlock();
}
}
}
I also have extended FutureTask to expose the set method, as follows:
class MyFutureTask<V> extends FutureTask<V> {
private MyCallable<V> myCallable;
public MyFutureTask(MyCallable<V> r) { super(r); myCallable = r;}
#Override
public void set(V x) { super.set(x); }
#Override
public void setException(Throwable t) { super.setException(t); }
#Override
protected void done() {
super.done();
myCallable.signal();
}
}
When the task is done, I signal the callable to stop it.
So every time a send is called, I create a new MyFutureTask, run it using an executor, save it in a map and return it.
When onMessageReceived is called I find the task in the map and set its result with the set method.
Is this a good approach?
And another question: is it a good approach to move the executor logic inside the task? I mean, to create a start method for it, which will run the task using the executor.
please advice.
I have a Java application that makes use of a Quartz Scheduler in the guise of a SchedulerFactoryBean. The main() method gets the application context, retrieves the root bean, and commences scheduling jobs.
The issue is that the Scheduler runs in its own thread, so when the main thread is done submitting jobs, it returns and the Scheduler goes on without it. When the Scheduler is finally done (or even if you explicitly call shutdown() on it), the application just hangs there for all eternity.
I have two solutions:
Keep track of the job/trigger count, incrementing it whenever you add a job to the Scheduler. Attach a simple SchedulerListener to the Scheduler that decrements this count with every call to triggerFinalized(), and set up a while loop with a Thread.sleep() inside it that constantly checks to see if the count has hit 0. When it does, it will return up to the main() method and the application will exit normally.
Take the custom SchedulerListener from option 1, and keep track of the job count inside of it. Increment for every call to jobAdded(), and decrement for every call to triggerFinalized(). When the count hits 0, call shutdown() on the Scheduler (or not, it doesn't actually matter) and then call System.exit(0).
I have implemented both of these independently in turn, so I know they both actually function. The problem is that they are both terrible. An infinite while loop polling a value? System.exit(0)? Bleargh.
Does someone have a better way, or are these seriously my only options here?
Edit: While thinking about this on the way home, I came to the conclusion that this may be caused by the fact that I'm using SchedulerFactoryBean. This auto-starts when Spring initializes the application context - that seems to put it outside the scope of the main thread. If I went with a slightly different Scheduler that I manually initialized and called start() on in the code, would this run the Scheduler in the main thread, thus blocking it until the Scheduler completes running all jobs? Or would I still have this problem?
Edit: Son of a...http://quartz-scheduler.org/documentation/quartz-2.x/examples/Example1
To let the program have an opportunity to run the job, we then sleep for 90 seconds. The scheduler is running in the background and should fire off the job during those 90 seconds.
Apparently, that will not work, because the scheduler seems to always run in the background.
In your SchedulerListener add an object solely for synchronization and locking. Call it exitLock or something. You main thread retrieves the scheduler, sets up the listener, submits all the jobs and then just before returning executes
Object exitLock = listener.getExitLock();
synchronized (exitLock) {
exitLock.wait(); // wait unless notified to terminate
}
On every triggerFinalized() call your listener decrements the counter for pending jobs. Once all the jobs have finished executing your listener shuts the scheduler down.
if (--pendingJobs == 0)
scheduler.shutdown(); // notice, we don't notify exit from here
Once the scheduler shuts down it invokes one last callback on the listener where we notify the main thread to terminate and hence the program exits gracefully.
void schedulerShutdown() {
// scheduler has stopped
synchronized (exitLock) {
exitLock.notify(); // notify the main thread to terminate
}
}
The reason we didn't notify in triggerFinalized() when all the pending jobs were finished is that in case the scheduler was shutdown prematurely and not all the jobs were finished we would have left our main thread hanging. By notifying in response to the shutdown event we make sure our program exits successfully.
I think here can be another solution.
Key points:
When task was executed the last time context.getNextFireTime() returns null.
Scheduler.getCurrentlyExecutingJobs == 1 indicate that it is the last executed job.
So when point 1 and 2 is true we can shutdown Scheduler and call System.exit(0).
Here is the code:
Listener
public class ShutDownListenet implements JobListener {
#Override
public String getName () { return "someName"; }
#Override
public void jobToBeExecuted (JobExecutionContext context) {}
#Override
public void jobExecutionVetoed (JobExecutionContext context) {}
#Override
public void jobWasExecuted (JobExecutionContext context, JobExecutionException jobException) {
try {
if (context.getNextFireTime() == null && context.getScheduler().getCurrentlyExecutingJobs().size() == 1) {
context.getScheduler().shutdown();
System.exit(0);
}
} catch (SchedulerException e) {
e.printStackTrace();
}
}
}
Code in the main function
public static void main (String[] args) {
Trigger trigger = ...
Job job = ...
JobListener listener = new ShutDownListenet();
scheduler.getListenerManager().addJobListener(listener);
scheduler.scheduleJob(job, trigger);
}
NOTE
I do not write synchronized blocks, but I tested this code with 100 concurent jobs, it works.
Did not tested in "complex" enviroment: clusters or RMI. (behavior can be differ).
Any comments are wellcome.
If your Quartz schedules/triggers are based on the database then you program needs to be alive till you would want to stop it. This can be doable like below. The idea is hook SchedulerListener and wait in the main thread. You need to hook your own way to terminate the program gracefully which completely a different topic itself.
public static void main(String[] args) {
AnnotationConfigApplicationContext appContext = // initialize the your spring app Context
// register the shutdown hook for JVM
appContext.registerShutdownHook();
SchedulerFactoryBean schedulerFactory = appContext.getBean(SchedulerFactoryBean.class);
scheduler = schedulerFactory.getScheduler();
final Lock lock = new ReentrantLock();
final Condition waitCond = lock.newCondition();
try {
scheduler.getListenerManager().addSchedulerListener(new SchedulerListener() {
#Override
public void jobAdded(JobDetail arg0) {
}
#Override
public void jobDeleted(JobKey arg0) {
}
#Override
public void jobPaused(JobKey arg0) {
}
#Override
public void jobResumed(JobKey arg0) {
}
#Override
public void jobScheduled(Trigger arg0) {
}
#Override
public void jobUnscheduled(TriggerKey arg0) {
}
#Override
public void jobsPaused(String arg0) {
}
#Override
public void jobsResumed(String arg0) {
}
#Override
public void schedulerError(String arg0, SchedulerException arg1) {
}
#Override
public void schedulerInStandbyMode() {
}
#Override
public void schedulerShutdown() {
lock.lock();
try {
waitCond.signal();
}
finally {
lock.unlock();
}
}
#Override
public void schedulerShuttingdown() {
}
#Override
public void schedulerStarted() {
}
#Override
public void schedulerStarting() {
}
#Override
public void schedulingDataCleared() {
}
#Override
public void triggerFinalized(Trigger arg0) {
}
#Override
public void triggerPaused(TriggerKey arg0) {
}
#Override
public void triggerResumed(TriggerKey arg0) {
}
#Override
public void triggersPaused(String arg0) {
}
#Override
public void triggersResumed(String arg0) {
}
});
// start the scheduler. I set the SchedulerFactoryBean.setAutoStartup(false)
scheduler.start();
lock.lock();
try {
waitCond.await();
}
finally {
lock.unlock();
}
} finally {
scheduler.shutdown(true);
}
}
If it helps someone else. I solved this by adding a shutdown-hook that triggers on Ctrl-C or normal kill (15) from script. A new Thread is spawned and polls the getCurrentlyExecutingJobs().size() every 3 seconds and exits when jobs counter has reached zero meaning all jobs finished.
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
try {
while (jobScheduler.getScheduler().getCurrentlyExecutingJobs().size() > 0) {
Thread.sleep(3000);
}
jobScheduler.getScheduler().clear();
} catch (Exception e) {
e.printStackTrace();
}
}));
while (!scheduler.isShutdown())
{
Thread.sleep(2L * 1000L);//Choose reasonable sleep time
}
I've got a inner class in my class doing some asynchronous processing and setting value on parent class. Ex :
class Myclass{
String test;
public getTestValueFromMyClass(){
//this starts asynchronous processing on my inner class
}
//inner class
class InnerClass extends TimerTask{
//doing something asynchronously, when this process is done
test = "somevalue";
}
}
Now here is the problem from Runner class :
class Runner{
public static void main(String[] args){
Myclass instance = new Myclass();
//This is always null because runner class doesn't wait for Inner class to
//complete asynchronous processing and to set test value
System.out.println(instance.getTestValueFromMyClass());
}
}
How do I get around this?
Others have suggested similar ideas but I'd use a single thread pool with a Callable.
Your class that is doing the asynchronous processing should implement Callable which will return the computed value. In this example it returns a String but it could also return your own object with more information.
public class MyClass implements Callable<String> {
public String call() {
//doing something asynchronously, when this process is done
return "somevalue";
}
}
Your Runner class would then create a thread pool, fire off the asynchronous task in the background, and then later wait for it to finish. When you submit a Callable job to the thread-pool, you get a Future class back which can be used to wait for the asynchronous job to finish and to get its return value.
public class Runner{
public static void main(String[] args) {
// you can use newFixedThreadPool(...) if you need to submit multiple
ExecutorService threadPool = Executors.newSingleThreadExecutor();
// you could store this future in a collection if you have multiple
Future<String> future = threadPool.submit(new MyClass());
// after submitting the final job, we _must_ shutdown the pool
threadPool.shutdown();
// do other stuff in the "foreground" while MyClass runs in the background
// wait for the background task to complete and gets its return value
// this can throw an exception if the call() method threw
String value = future.get();
System.out.println(value);
}
}
Evidently, you have to make getTestValueFromMyClass to wait for InnerClass execution. This can be done with some synchronization facility (Semaphore, CountdownLatch, BlockingQueue...). But most straightforward is to use java.util.concurrent.ScheduledThreadPoolExecutor instead of java.util.Timer. Its method schedule(Callable<V> callable, long delay, TimeUnit unit) returns Future, and Future.get() waits for and returns the computed value.
One very simple mechanism is to use a BlockingQueue to communicate between your threads. Here I am creating the queue in the thread class but it could just as easily be created in the caller and passed to the thread.
public class Runner {
static class MyClass implements Runnable {
// Thread will post to this queue when it completes.
BlockingQueue q = new ArrayBlockingQueue(1);
// Call to wait for the post.
public void waitForFinish() throws InterruptedException {
// Just take! This will wait until something gets posted.
q.take();
}
#Override
public void run() {
try {
// Just wait ten seconds.
Thread.sleep(10000);
} catch (InterruptedException ex) {
// Just exit when interrupted.
} finally {
try {
// Signal finished.
q.put("Done");
} catch (InterruptedException ex) {
// Just exit when interrupted.
}
}
}
}
public static void main(String[] args) throws InterruptedException {
// Make my instance.
MyClass instance = new MyClass();
// Fire it off.
new Thread(instance).start();
// Wait for it to finish.
instance.waitForFinish();
// All done.
System.out.println("Finished");
}
}
You could use a handler and post a message when processing is done!
class Myclass{
// pre initialize thread pool
private static ExecutorService executor = Executors.newFixedThreadPool( 5 );
private String test;
public String getTestValueFromMyClass() throws Exception {
// start asynchronous calculations
Future<String> resultHandler =
executor.submit( new Callable<String>() {
#Override
public String call() throws Exception {
return "Asynchronously calculated result";
}
} );
// do something in current thread
// ...
// wait until asynchronous task ends, get result
// and assign it to instance variable
this.test = resultHandler.get();
return test; // returns string "Asynchronously calculated result"
}
}