public boolean tryAcquire(long timestampNanos) {
        int tokenCount = tokens;
        if (tokenCount > 0) {
            if (TOKENS_UPDATER.compareAndSet(this, tokenCount, tokenCount - 1)) {
                return true;
            }
            return tryAcquire(timestampNanos);
        }
        long refillIntervalNanos = context.refillIntervalNanos;
        if (refillIntervalNanos <= 0) {
            return false;
        }
        int periods = MathUtil.toInt((timestampNanos - lastRefillTimeNanos) / refillIntervalNanos);
        if (periods <= 0) {
            return false;
        }
        // Try to refill.
        // We expect tokensPerPeriod is always greater than 0,
        // so tokenCount can always be greater than or equal 0.
        int capacity = context.capacity;
        tokenCount = MathUtil.multiply(periods, context.tokensPerPeriod, capacity) - 1;
        if (tokenCount > capacity) {
            tokenCount = capacity - 1;
        }
        if (TOKENS_UPDATER.compareAndSet(this, 0, tokenCount)) {
            lastRefillTimeNanos = timestampNanos;
            return true;
        }
        return tryAcquire(timestampNanos);
    }
@implNote We don't validate properties here, and it should be validated when the properties
          are updated
turms-server-common/src/main/java/im/turms/server/common/infra/throttle/TokenBucket.java