使用同步地图记住您访问每台服务器的时间。
一种方法是检查之前的访问时间,如有必要,在方法本身中休眠:
private static final Map<URL, Instant> lastAccessTimes = new HashMap<>();
private static final Duration COOLDOWN = Duration.ofSeconds(60);
public void contact(URL url)
throws IOException,
InterruptedException {
URL server = new URL(url.getProtocol() + ":" + url.getAuthority());
Instant now = Instant.now();
long delay = 0;
synchronized (lastAccessTimes) {
Instant newAccessTime = now;
Instant lastAccess = lastAccessTimes.get(server);
if (lastAccess != null) {
Instant soonestAllowed = lastAccess.plus(COOLDOWN);
if (now.isBefore(soonestAllowed)) {
newAccessTime = soonestAllowed;
delay = now.until(soonestAllowed, ChronoUnit.NANOS);
}
}
lastAccessTimes.put(server, newAccessTime);
}
if (delay > 0) {
TimeUnit.NANOSECONDS.sleep(delay);
}
URLConnection connection = url.openConnection();
// etc.
}
如果您不想冒着阻止线程池的风险,可以在冷却期后使用执行程序重试。比如使用 CompletableFuture 的默认线程池:
private static final Map<URL, Instant> lastAccessTimes = new HashMap<>();
private static final Duration COOLDOWN = Duration.ofSeconds(60);
public void contact(URL url) {
URL server;
try {
server = new URL(url.getProtocol() + ":" + url.getAuthority());
} catch (MalformedURLException e) {
logger.log(Level.WARNING,
"Could not extract server from " + url, e);
return;
}
Instant now = Instant.now();
synchronized (lastAccessTimes) {
Instant lastAccess = lastAccessTimes.get(server);
if (lastAccess != null) {
Instant soonestAllowed = lastAccess.plus(COOLDOWN);
if (now.isBefore(soonestAllowed)) {
long delay = now.until(soonestAllowed, ChronoUnit.NANOS);
CompletableFuture.runAsync(() -> contact(url),
CompletableFuture.delayedExecutor(
delay, TimeUnit.NANOSECONDS));
return;
}
}
lastAccessTimes.put(server, now);
}
try {
URLConnection connection = url.openConnection();
// etc.
} catch (IOException e) {
logger.log(Level.WARNING, "Could not contact " + url, e);
}
}