3

I've been doing some testing in an app that I've been involved with for the past little while, and it appears as though some of the code we've written is causing a race condition.

What's happening is something like this:

//We make this call which resolves a task in a BPM service
//causing a process instance to flow to the next person/task
ServiceRemoteAPI.completeTask(taskId);

//We then make this call which depends on the complete resolution of the 
//original call.
Tasks userTasks = ServiceRemoteAPI.tasksUserHasAvailable(nextUser);

//And we use those tasks to send an e-mail
for(Task task : userTasks){
    if(task == rightTask){
       sendEmail();
    }
}

Problem is, it looks like in some cases the original remote API call isn't resolving completely before we're making the second one, and that's causing erratic behavior. So I was planning on doing something like this:

ServiceRemoteAPI.completeTask(taskId);

**Thread.sleep(5000);**

Tasks userTasks = ServiceRemoteAPI.tasksUserHasAvailable(nextUser);

for(Task task : userTasks){
    if(task == rightTask){
       sendEmail();
    }
}

The question is, though, that I haven't done a lot of threading before, and based on my research this seems to be appropriate usage of Thread.sleep(), but I wonder if there's anything I'm missing, or if there is a more robust way to accomplish what I'd like to achieve here.

  • 2
    It's always good to know that Thread.sleep can wake up any time, not only after the timeout given in the parameter, so if you want to use it, you should do it in a while loop, checking for some condition to happen. This specific of Java threads is called "spurious wakeup". See stackoverflow.com/questions/1050592/… – Ivan Gammel Dec 17 '15 at 13:16
6

Never sleep a thread for X time to give the API time to process something. This might seem to fix the problem, but it doesn't prevent race conditions, even though in all your testing you never see the race condition again. It's just not guaranteed.

In my experience, Thread.sleep() is a terribly useless function.

What you should do is pause the main thread (or whatever thread is making the 2nd call) until you get the response from the first thread (your first API call).

Google semaphore and read up a bit. Basically you don't give permission to the other thread to run until the current one is done doing it's critical section (any processing that needs to occur). You can think of current here to mean your first request to the API, and the other to be the thread that launches/waits for the 2nd API call.

  • Yea that's kind of what I figured. The first service call doesn't seem to have a way of confirming complete resolution so I think I'll have to do a check on the results of the second service call. OTOH, a sleep is conceptually easier, and I'd assume it's failure would be a very rare case. Not ideal, obviously, but not a terrible solution. – Canadian Coder Dec 17 '15 at 19:37
  • @CanadianCoder If you're going to do that, you'll introduce (unnecessary) delays in your code; that said, I would highly recommend fixing the API as soon as possible to include status as part of the response so that you can actually know what action to take and when in the code you posted in your question. If you never get around to that, you'll start losing track of why you put these 'hotfixes' in place, degrading quality. Just some food for thought ;) any good API will include status/progress information in responses in order to prevent clients (your code) from breaking like this. – Chris Cirefice Dec 17 '15 at 23:05
1

As an alternative to @Chris' fine suggestion: Queue the email requests. That way they always go out in order. For example, you send a "let's meet at 12:30" followed by "oops - meeting went late, meet at 1:00".

If the order is preserved, the recipient should understand. If the order is indeterminate there's an excellent chance they will show up at the wrong time.

That's a trivial example, but, seriously, we had a highly complex multi-threaded multi-core application, and most every race-condition or out-of-order problem could be solved by adding another queue.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.