Hi guys i just picked up spring boot and would like some advice!
I needed to create an object that would travel from one position to another, through mathematically calculated increments. And once the “start” rest call is executed it would run need to run forever patrolling(for this i implemented an infinite while loop with the condition set to true in the start rest call, and false on the end rest call) between two-three pre-designated points until the “stop” rest call is called.
This rest call also creates up to 20 objects concurrently, and since there is an infinite while loop,I cant just do a for loop to create 20 of these objects and it will be stuck on the first iteration being a blocking code due to the infinite while loop.
To fix this i used java threads so that i can create any number of these objects concurrently. Since the thread.stop() is deprecated, i used something like service.setIsThreadRunning(false) for my controller, and for my service layer
I used a while(isThreadRunning) in my run() method.
have tried using the interrupt way to close the threads but it does not seem to close it at all, the way which worked for me was using a global variable.
Where isThreadRunning is a global variable being set to false once the end rest call is called.
To my understanding, global variables are shared even among different threads, so by changing this global variable the stop method worked for me without issue.
All of this worked locally without issue.
The main problem comes when i tried to deploy this spring boot application in a jar file, where there was a bug where the closing the jar application will still result in the created threads continuing to run. Despite calling the end rest call multiple times.
Based on my searches it seems that a shutdown hook, setting created daemon to true could potentially fix it. If possible is there another explanation on why this happens? Is there a unique interaction of threads with the jar file that i am not considering?
My speculation is that it is caused by someone not executing the end rest call before closing the jar file, then when it is started again it will be in a new memory location, so any new “end rest calls” would not be able to reach it.
For a fix I have tried adding a shutdown hook to set the variables to false in case someone forgets to do the end rest call, and setting the created daemon threads to true.
I understand putting a infinite while loop is not recommended in java but i honestly am open to recommendations on alternative methods to suite these requirements. For other approaches I have tried spring reactive programming(Mono , flux ) architecture(seems to get stuck in the infinite while loop),@Async.
Would greatly appreciate it if someone could give me a direction on what i can look into to approach this!
The code looks something like this for the thread implementation: Service layer:
public class Service extends Thread{
//global variable
@Setter
private boolean isThreadRunning=false;
@Override
public void run(){
while(isThreadRunning){
try{
moveObject();
}
catch{
log.error("ending movement");
}
}
}
note* this is a psuedoish version of the code as i cant copy paste it,i typed this out from scratch and memory so some parts like the math portion which is not the main issue i will not write much detail about it. All there is needed to be known about the math part is that the new position will be incremented and sent to the front end every 3 seconds to "move" the object. hence the infinite while loop and the object is supposed to move endlessly until the end rest call is executed.
The problematic infinite while loop, as i am not allowed to copy the code, this is the general structure of it :
//pos consists of latitude, longitude
public void moveObject() throws InterruptedException{
MovingObject obj = new MovingObject();
while(isThreadRunning){
for(int i =0;i<=iterations;i++){
if(isThreadRunning== false){
return;
}
else{
MathNewIncrementedPos();
}
}
}
}
Controller:
@Log4j2
@RestController
@RequiredArgsConstructor
@RequestMapping("/test")
public class Controller{
private final Service service;
ArrayList<Thread> threadList = new ArrayList<Thread>();
@PostMapping("/moveObject")
public void move(@PathVariable Integer noOfObj) throws InterruptedException{
for(int i=0;i<noOfobj;i++){
Thread newThread = new Thread(service);
newThread.setDaemon(true);
newThread.start();
threadList.add(newThread);
threadList.get(i).start();
}
}
@PostMapping("/endMovement")
public void endMove() throws InterruptedExecption{
int size = threadList.size();
service.setisThreadRunning(false);
//i dont think this is needed, but i just do it to try ensure all
//threads are closed
for(int i=0;i<size;i++){
service.setisThreadRunning(false);
}
threadList.clear();
}
}
and lastly, a shutdown hook to ensure that even the end rest call is not executed, all threads should close before shutting down
@PreDestroy
public void onExit(){
service.setisThreadRunning(false);
}
Is this possible to do in spring reactive programming? (using Mono/flux) as I am unsure how to wrap this in a reactive way.