Replacing busy loop and sleep in thread
Replacing busy loop and sleep in thread
I have a Thread that calls a certain service.
The service defines a callback function that can be called multiple times
as long as there is data onProcessing().
The onCompletion() is called once it is finished.
public CallThread implements Runnable{
public boolean isCompleted = false;
public void run(){
ResponseObserver response = new ResponseObserver(){
public void onException(){
//Errors could happen sometimes so the program should just issue another request
}
public void onCompletion(){
isCompleted = true;
//process the result
}
public void onProcessing(){
//This could be called multiple time
}
}
//Service is the object that calls the service
Service service = getService();
while(!isCompleted){
//Request object is populated
Request request = supplyRequest();
service.getDetails(request, response);
//How to remove this sleep
Thread.sleep(10 * 1000);
}
}
}
I have created a busy loop that checks for the isCompleted and having it sleep.
What I am doing now..is to use sleep command to be able for the function to be completed
until issuing a next request.
I am not sure if this is optimal as sometimes..it does not take 10 seconds before
onCompletion() is called.
I just wanted the service to return something before I could issue another request.
Is there a way to optimized my code?
service.getDetails()
sleep()
getDtails()
onCompletion()
Yes..the callback function could return an onException..say for example the dependent server is down... so in that case I should re-issue a new request.
– Mark Estrada
Jul 1 at 7:21
In that case too, thread is not needed, you just have to call the service in
onException
method.– 11thdimension
Jul 1 at 7:22
onException
Ohh sorry..forgot to mention that this thread is being spawn by the main thread..so it is needed that this should run into completion... the main thread cannot proceed until this thread is finished
– Mark Estrada
Jul 1 at 7:24
2 Answers
2
It's probably going to be some combination of CountDownLatch
. Try following
CountDownLatch
public class CallThread implements Runnable {
private final CountDownLatch completionLatch = new CountDownLatch(1);
public void run(){
callService();
//Wait without timeout, bad idea
try {
completionLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void callService() {
//Service is the object that calls the service
Service service = getService();
//Request object is populated
ResponseObserver response = new MyResponseObserver(completionLatch);
Request request = supplyRequest();
service.getDetails(request, response);
}
class MyResponseObserver {
private CountDownLatch completionLatch;
MyResponseObserver(CountDownLatch latch) {
this.completionLatch = latch;
}
public void onException(){
/* Retry on exception */
callService();
}
public void onCompletion(){
completionLatch.countDown();
//process the result
}
public void onProcessing(){
//This could be called multiple time
}
};
}
Besides that you can probably also consider using Callable
instead of Runnable
since main
is waiting for the processing, probably its better to do in main thread itself. Below is a what it would look like
Callable
Runnable
main
public CallThread implements Callable<MyResult> {
private MyResult myResult;
private final CountDownLatch completionLatch = new CountDownLatch(1);
public MyResult call(){
callService();
//Wait without timeout, bad idea
try {
completionLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
return myResult;
}
public setMyResult(MyResult myResult) {
this.myResult = myResult;
}
public void callService() {
//Service is the object that calls the service
Service service = getService();
//Request object is populated
ResponseObserver response = new MyResponseObserver(completionLatch);
Request request = supplyRequest();
service.getDetails(request, response);
}
class MyResponseObserver {
private CountDownLatch completionLatch;
MyResponseObserver(CountDownLatch latch) {
this.completionLatch = latch;
}
public void onException(){
/* Retry on exception */
callService();
}
public void onCompletion(){
completionLatch.countDown();
//process the result
setMyResult(result);
}
public void onProcessing(){
//This could be called multiple time
}
};
}
in main()
...
FutureTask task = new FutureTask(new CallThead());
new Thread(task).start();
MyResult result = task.get();//blocks thead
Question, in the first await method of using Runnable...If I put a time out there completionLatch.await(5, Timeout.Seconds); And the timeout happened.. isn't my run method would be completed and my thread will end?
– Mark Estrada
Jul 1 at 8:00
I'm still modifying the answer so please bear with me, regarding the question. A timeout is supposed to be an irrecoverable event for the latch. It's to avoid having an infinitely running blocked thread. For example you could probably set it to time out after 12 hours or something.
– 11thdimension
Jul 1 at 8:02
Thank you mate.. Learned a lot from this post.
– Mark Estrada
Jul 1 at 11:24
A CountDownLatch
or CompletableFuture
can be used to wait for a condition asynchronously:
CountDownLatch
CompletableFuture
public CallThread implements Runnable {
public boolean isCompleted = false;
@Override
public void run() {
// Try up to 5 calls
for (int i = 0; i < 5; ++i) {
// Create the observer
MyResponseObserver observer = new MyResponseObserver();
// Call the service
service.getDetails(supplyRequest(), observer);
try {
// Wait for the result
Boolean result = observer.completable.get();
// Or wait with a timeout
// observer.completable.get(30, TimeUnit.SECONDS);
// Completed successfully
isCompleted = true;
return;
} catch (ExecutionException e) {
// Completed with exception. Retry.
} catch (InterruptedException e) {
return;
}
}
}
/** Response callback handler */
private static class MyResponseObserver implements ResponseObserver {
/** Result object (change Boolean to the type of your result) */
public final CompletableFuture<Boolean> completable = new CompletableFuture<>();
@Override
public void onException() {
// Signal an error
completable.completeExceptionally(new Exception("Error"));
}
@Override
public void onCompletion() {
// Signal a result
completable.complete(true);
}
@Override
public void onProcessing() {
}
}
}
I have thought of using this latch..but the problem is that onException could happen several times. If that is the case then, it will not retry the call again.
– Mark Estrada
Jul 1 at 7:22
Also, a latch cant be reset right? Or it is possible but is it a good way on how it was designed to be used?
– Mark Estrada
Jul 1 at 7:42
Sorry I don't get what you mean..can you show me some code? Where can I do a retry, is it inside my busy loop....also, do I not need to do a reset of the latch? I am not that verse in java multithreading.
– Mark Estrada
Jul 1 at 7:48
Say for example, in my while loop..I waited for 30 sec and an error occurs..the onException is called.. then how can I retry my transaction?
– Mark Estrada
Jul 1 at 7:54
Thank you mate.. Learned a lot from this post.
– Mark Estrada
Jul 1 at 11:24
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
Is there a need to call
service.getDetails()
multiple times for the same request? If not there's no need forsleep()
, as there's nothing meaningful happening in the thread itself aftergetDtails()
call that will be dependent ononCompletion()
, it doesn't make sense to keep it blocked.– 11thdimension
Jul 1 at 7:11