0

I'm currently using a third-party API to fetch results, using a REST client (Retrofit).

The problem is that the amount of requests per second that I can do to the API is limited with interval: I can't do more than 10 requests per second with 100 ms between requests.

As I want to multi-thread those calls for performance purpose, I wonder how could I manage this interval of 100 ms between each API call?

I can't sleep 100 ms in a single thread since it's bounded to the current thread.

Is there any way to use a shared sleep between multiple threads? Or do you have any idea how to implement this?

Thanks.

9
  • You want the execution to overlap, right? So ScheduledThreadPoolExecutor won't work for you. If that is the case... why? Surely you can make the request asynchronously fast enough so that overlapping is not a problem. Then even Timer would work.
    – Theraot
    Nov 21, 2019 at 12:06
  • You only need threading in handling the responses, if at all. Making the requests can be centralised.
    – Caleth
    Nov 21, 2019 at 13:10
  • @Theraot : Some threads in my app will call 3-rd party API through a singleton REST client. I want that no matter which thread calls this client, an overlap of 100ms between calls. I do not think that timer will do the Job or I misunderstood something.
    – Rouliboy
    Nov 21, 2019 at 13:31
  • @Caleth : multiple threads call call the REST client.
    – Rouliboy
    Nov 21, 2019 at 13:31
  • @Rouliboy overlap means that it will awake a thread to make a request even if there are other threads that haven't ended. That is, if you have an interval of 100ms without overlap, they will queue up when it takes too long to make the request. On the other hand, an interval of 100ms with overlap can have multiple threads requesting at the same time when they take longer than 100ms to make the request. If you do not want overlap (i.e. you want them to queue up), then Timer will work. Now, are requests slow enough that overlap matters? Using Timer, there would be no queue, if requests are fast.
    – Theraot
    Nov 21, 2019 at 13:46

1 Answer 1

2

If I understand correctly, you have multiple endpoints and multiple users making requests concurrently.

Then you want to make sure that these requests happen at most at some rate, globally.


This is the basic idea:

You can add the requests you want to do to a queue. More precisely a ConcurrentLinkedQueue of Runnable (you will create custom runnables for each endpoint). Multiple threads can interact with this single queue concurrently.

Then you can use a Timer to execute a command that takes one item from the queue and executes it. This way each interval a request is made, regardless of the rate at which they are added to the queue (unless it is empty, of course).

Replace Timer with ScheduledThreadPoolExecutor if needed.


You can improve upon this by stopping the Timer (or stopping the ScheduledFuture) if you are in a period of inactivity※. Plus, starting/scheduling it again when adding items to the queue (if it was stopped).

※: Keep track of the time of the last request. If you took something from the queue, update the time to the current time. If you did not, compare the time to the current time... if too much (for some definition of too much) time has passed, then you are in a period of inactivity.


I'm not sure how do you handle responses. However, independently of whatever or not they are synchronous. You have the option to pass a custom Consumer where you can pass the result. In fact, your custom Runnable (the one you add to the queue) could hold the Consumer from the client.

1
  • Seems really nice, thanks for the clear explanations and I love that solution!
    – Rouliboy
    Nov 21, 2019 at 15:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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