You can make a simple adaption to the map approach to solve your problem. First, I show some code, and I will explain it afterwards:
class Scheduler {
private final Map<String, Long> map = new HashMap<>();
private final long interval = 200; // ms
public void sleepAsLongAsNecessary(String key) {
long now = System.currentTimeMillis();
long deadline = next(now, key);
while (now < deadline) {
Thread.sleep(deadline - now);
now = System.currentTimeMillis();
}
}
private synchronized long next(long now, String key) {
Long oldValue = map.get(key);
long newValue = oldValue == null ? now : Math.max(oldValue + interval, now);
map.put(key, newValue);
return newValue;
}
}
This looks pretty similar to the solution you described. The important difference is:
The map does not store the last request time. Instead, it stores the planned request time of the last thread, that asked for a request time. If several threads ask for a request time at the same time, each of them will get a different planned request time.
As soon as the threads knows its planned request time, it will sleep as long as necessary.