@ThreadSafety(level=COMPLETELY_THREADSAFE) public final class FixedRateBarrier extends java.lang.Object implements java.io.Serializable
Once a class is constructed with the duration of an interval and the target
per interval, the await()
method only releases callers at the
specified number of times per interval. This class is most useful when
the target number per interval exceeds the limits of other approaches
such as java.util.Timer
or
java.util.concurrent.ScheduledThreadPoolExecutor
. For instance,
this does a good job of ensuring that something happens about 10000 times
per second, but it's overkill to ensure something happens five times per
hour. This does come at a cost. In the worst case, a single thread is
tied up in a loop doing a small amount of computation followed by a
Thread.yield(). Calling Thread.sleep() is not possible because many
platforms sleep for a minimum of 10ms, and all platforms require sleeping
for at least 1ms.
Testing has shown that this class is accurate for a "no-op"
action up to two million per second, which vastly exceeds its
typical use in tools such as searchrate
and modrate
. This
class is designed to be called by multiple threads, however, it does not
make any fairness guarantee between threads; a single-thread might be
released from the await()
method many times before another thread
that is blocked in that method.
This class attempts to smooth out the target per interval throughout each interval. At a given ratio, R between 0 and 1, through the interval, the expected number of actions to have been performed in the interval at that time is R times the target per interval. That is, 10% of the way through the interval, approximately 10% of the actions have been performed, and 80% of the way through the interval, 80% of the actions have been performed.
It's possible to wait for multiple "actions" in one call with
await(int)
. An example use is rate limiting writing bytes out to
a file. You could configure a FixedRateBarrier to only allow 1M bytes to
be written per second, and then call await(int)
with the size of
the byte buffer to write. The call to await(int)
would block until
writing out the buffer would not exceed the desired rate.
Constructor and Description |
---|
FixedRateBarrier(long intervalDurationMs,
int perInterval)
Constructs a new FixedRateBarrier, which is active until
shutdownRequested is called. |
Modifier and Type | Method and Description |
---|---|
boolean |
await()
This method waits until it is time for the next 'action' to be performed
based on the specified interval duration and target per interval.
|
boolean |
await(int count)
This method waits until it is time for the next
count 'actions'
to be performed based on the specified interval duration and target per
interval. |
ObjectPair<java.lang.Long,java.lang.Integer> |
getTargetRate()
Retrieves information about the current target rate for this barrier.
|
boolean |
isShutdownRequested()
Returns
true if shutdown has been requested. |
void |
setRate(long intervalDurationMs,
int perInterval)
Updates the rates associated with this FixedRateBarrier.
|
void |
shutdownRequested()
Shuts down this barrier.
|
public FixedRateBarrier(long intervalDurationMs, int perInterval)
shutdownRequested
is called.intervalDurationMs
- The duration of the interval in milliseconds.perInterval
- The target number of times that await()
should
return per interval.public void setRate(long intervalDurationMs, int perInterval)
intervalDurationMs
- The duration of the interval in milliseconds.perInterval
- The target number of times that await()
should
return per interval.public boolean await()
true
if shutdown has been requested and false
otherwise.public boolean await(int count)
count
'actions'
to be performed based on the specified interval duration and target per
interval. To achieve the target rate, it's recommended that on average
count
is small relative to perInterval
(and the
count
must not be larger than perInterval
). A
count
value will not be split across intervals, and due to timing
issues, it's possible that a count
that barely fits in the
current interval will need to wait until the next interval. If it's not
possible to use smaller 'count' values, then increase perInterval
and intervalDurationMs
by the same relative amount. As an
example, if count
is on average 1/10 as big as
perInterval
, then you can expect to attain 90% of the target
rate. Increasing perInterval
and intervalDurationMs
by
10x means that 99% of the target rate can be achieved.
This method can be called by multiple threads simultaneously. This method returns immediately if shutdown has been requested.
count
- The number of 'actions' being performed. It must be less
than or equal to perInterval
, and is recommended to
be fairly small relative to perInterval
so that it
is easier to achieve the desired rate and exhibit smoother
performance.true
if shutdown has been requested and false
otherwise.@NotNull public ObjectPair<java.lang.Long,java.lang.Integer> getTargetRate()
Long
that specifies the duration of
the current interval in milliseconds and an Integer
that specifies
the number of times that the await()
method should return per
interval.public void shutdownRequested()
public boolean isShutdownRequested()
true
if shutdown has been requested.true
if shutdown has been requested and false
otherwise.