Creating delay between threads

335 Views Asked by At

All, I have an api call which is called by many threads. The only issue is that the delay bet. threads should be at least 1 second. I realized - w/o the synchronized block - if one thread is calling the api at time t1, then all other threads wait for 1 second and then all other threads call the api at t1 + 1 second. This is not I want, so I put the whole wait block in synchronized block that as long as one thread is waiting all other threads block.

This works; however, I think its not the most efficient way to do this.

Any recommendations is greatly appreciated.

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
1

There are 1 best solutions below

1
On BEST ANSWER

If you want allow to call API on some rate. Also you don't need volatile with static Atomic. And you don't need Atomic if you work with them in synch blocks.

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.
}