I have a Java UI service that has an API method that invokes an operation that's relatively slow (say ~30secs). The operation is parameterless, but it operates on external data that does change (relatively slowly) over time. It's not critical for the method to return the most up-to-date results - if they're 30secs old that's acceptable.
Ultimately I need to optimise the implementation of the slow operation, but as a short-term fix, I'd like to make the operation mutually exclusive, such that if a second incoming request (on a separate thread) attempts to invoke the operation while another is already in progress, then the second one blocks until the first one completes. The second thread then uses the results of the first invocation of the operation - i.e. it doesn't attempt to run the operation again.
E.g.:
class MyService {
String serviceApiMmethod() {
// If a second thread attempts to call this method while another is in progress
// then block here until the first returns and then use those results
// (allowing it to return immediately without a second call to callSlowOperation).
return callSlowOperation();
}
}
What's the preferred general approach for this in Java (8). I'm guessing I could use a CountDownLatch, but it's not clear how best to share the result across the threads. Is there an existing concurrency primitive that facilitates this?
EDIT: I need to clear any references to the result once all threads have consumed it (i.e. returned it to the caller), as it's relatively large object, which needs to be GC'ed as soon as possible.
ReentrantReadWriteLock
will be easier to use:Here, read lock protects against reading stale state, and write lock protects against performing "slow operation" by multiple threas simulateneously.
Another approach (which I think might be even better) would be to add all threads that ask for result in a synchronized collection. Then when the result arrives - send the responses back to the threads. You can use the java 8 functional interface consumer to make it more fancy. It will not waste CPU time (like with thread.sleep or even with countDownLatch and other modern java concurrent classes). It would require these thread to have a callback method to accept the result but it might even make your code easier to read:
And the calling threads:
This way the resulting object will be garbage collected because the all consumers will get it right away and release it.
And to test the results:
And the result:
All threads got their result after 1 second. Kind of reactive programming ;) It does change the way into a more asynchronous way but if the caller of the thread wants to block the execution while getting the result he can achieve it. The service basically is self explanatory about what is done. It's like saying "my operation is slow so instead of running the call for each of you callers, I will send you the result once I am ready - give me a consumer method"
Simple idea
Version 1:
Version 2: together with @AleksandrSemyannikov
As a solution you can use something like this:
Note that MyService must be singleton and ResultHolder must be immutable