code: synchronized long tryReserveTokens(long tokens, long maxWaitMillis) {
      long now = System.currentTimeMillis();
      long waitUntilNextTokenAvailable = Math.max(0, this.nextTokenAvailableMillis - now);
  
      updateTokensStored(now);
      if (tokens <= this.tokensStored) {
        this.tokensStored -= tokens;
        return waitUntilNextTokenAvailable;
      }
  
      double additionalNeededTokens = tokens - this.tokensStored;
      // casting to long will round towards 0
      long additionalWaitForEnoughTokens = (long) (additionalNeededTokens / this.tokensPerMilli) + 1;
      long totalWait = waitUntilNextTokenAvailable + additionalWaitForEnoughTokens;
      if (totalWait > maxWaitMillis) {
        return -1;
      }
      this.tokensStored = this.tokensPerMilli * additionalWaitForEnoughTokens - additionalNeededTokens;
      this.nextTokenAvailableMillis = this.nextTokenAvailableMillis + additionalWaitForEnoughTokens;
      return totalWait;
    }
docs: Note: this method should only be called while holding the class lock. For performance, the lock is not explicitly
  acquired.
  
  @return the wait until the tokens are available or negative if they can't be acquired in the give timeout.