public long acquire() {
        final long currOpIndex = V_TIME_UPDATER.getAndIncrement(this);
        long start = this.start;
        if (start == Long.MIN_VALUE) {
            start = nanoClock.get();
            if (!START_UPDATER.compareAndSet(this, Long.MIN_VALUE, start)) {
                start = this.start;
                assert start != Long.MIN_VALUE;
            }
        }
        long intendedTime = start + currOpIndex * intervalNs;
        long now = nanoClock.get();
        // If we are behind schedule too much, update V_TIME
        if (now > intendedTime + MAX_V_TIME_BACK_SHIFT_SEC * ONE_SEC_IN_NS) {
            long newVTime = (now - start) / intervalNs;
            // no need to CAS, updated by multiple threads is acceptable
            V_TIME_UPDATER.set(this, newVTime + 1);
            intendedTime = start + newVTime * intervalNs;
        }
        return intendedTime;
    }
@Test
    void acquireSlowSingleThread() {
        Supplier<Long> mockClock = mock(Supplier.class);
        when(mockClock.get()).thenReturn(SECONDS.toNanos(2));
        UniformRateLimiter rateLimiter = new UniformRateLimiter(1000, mockClock);
        assertThat(rateLimiter.acquire()).isEqualTo(2000000000L);
        assertThat(rateLimiter.acquire()).isEqualTo(2001000000L);
        assertThat(rateLimiter.acquire()).isEqualTo(2002000000L);
    }