0

Все, у меня есть вызов api, который вызывается многими потоками. Единственная проблема заключается в том, что ставка задержки. потоки должны быть не менее 1 секунды. Я понял - без синхронизированного блока - если один поток вызывает api в момент времени t1, тогда все остальные потоки ожидают 1 секунду, а затем все остальные потоки вызовут api при t1 + 1 секунду. Этого я не хочу, поэтому я помещаю весь блок ожидания в синхронизированный блок, если один поток ожидает всех остальных потоков.Создание задержки между потоками

Это работает; однако, я думаю, что это не самый эффективный способ сделать это.

Любые рекомендации приветствуются.

private static volatile AtomicLong lastAPICall = new AtomicLong(); 

private void callAPI() { 

    // 1 sec plus a little extra 
    final long oneMS = 1 * 1000 + 100;    
    long lastCall = 0; 
    long timeDiff = 0; 

    synchronized (lastAPICall) { 
     timeDiff = System.currentTimeMillis() - lastAPICall.get(); 
     lastCall = lastAPICall.getAndSet(System.currentTimeMillis()); 
    } 
} 

if (System.currentTimeMillis() - lastCall < oneMS) { 
    synchronized (lastAPICall) { 
      try { 
       long sleep = oneMS - timeDiff; 
       Thread.sleep(oneMS - timeDiff); 
      } catch (InterruptedException ignore) {} 
      finally { 
       lastAPICall.set(System.currentTimeMillis()); 
       log.info("Thread: " + Thread.currentThread().getId() + " calling the api at this time: " + System.currentTimeMillis()); 
     } 
    } 
} 

try { 
// API CALL 
} 
catch (IOException t){ 
      throw t; 
} finally { 
    synchronized (lastAPICall) { 
    lastAPICall.set(System.currentTimeMillis()); 
    } 
} 

// Log files for running the code with 4 threads 
Thread: 35 calling the api at this time: 1456182353694 
Thread: 34 calling the api at this time: 1456182354795 
Thread: 37 calling the api at this time: 1456182355905 
Thread: 36 calling the api at this time: 1456182357003 
+0

Вы хотите, чтобы каждый поток должен был ждать одну секунду после того, как предыдущий поток начал свой вызов, или после того, как предыдущий поток завершил свой вызов? –

+0

Я хочу, чтобы каждый поток подождал одну секунду после того, как предыдущий поток начал свой вызов. – blueSky

+0

Не имеет значения, завершен ли первый вызов или нет. – blueSky

ответ

0

Если вы хотите разрешить вызов API на определенном уровне. Также вам не нужна летучесть со статическим атомом. И вам не нужен Atomic, если вы работаете с ними в блоках синхронизации.

private static final long MAX_RATE = 1000; 
private static final Semaphore API_CALL_SEMAPHORE = new Semaphore(1); 
private volatile long lastCall; 

public void callApi() throws IOException, InterruptedException { 
    try { 
     API_CALL_SEMAPHORE.acquire(); 
     delayedCall(); 
    } catch (IOException | InterruptedException e) { 
     throw e; 
    } finally { 
     API_CALL_SEMAPHORE.release(); 
    } 
} 

private void delayedCall() throws InterruptedException, IOException { 
    long tryCallTime = System.currentTimeMillis(); 
    final long deltaTime = tryCallTime - lastCall; 
    if (deltaTime < MAX_RATE){ 
     final long sleepTime = MAX_RATE - deltaTime; 
     Thread.sleep(sleepTime); 
     tryCallTime += sleepTime; 
    } 
    // API CALL 
    lastCall = tryCallTime; // if you want to delay only succeed calls. 
} 
+0

Код, добавленный Retardust, гарантирует, что только один поток вызывает API в течение отложенного времени, где необходимо, чтобы вызвать API несколькими потоками одновременно, таким образом, что вызывает задержку на 1 секунду. Несколько потоков могут вызывать API независимо от того, что предыдущие вызовы завершены или нет. Вышеприведенный код можно легко изменить, чтобы выполнить это. Спасибо за ответ. – blueSky

Смежные вопросы