This question already has answers here:
How do I write a correct micro-benchmark in Java?
(11 answers)
Closed 4 years ago.
please i need help i am writing this code to be able to display my execution time anytime i run a program but i usually get different time even when its the same input
after importing all this
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
/** Class KnuthMorrisPratt **/
public class Knuth1
{
/** Failure array **/
private int[] failure;
/** Constructor **/
public Knuth1(String text, String pat)
{
/** pre construct failure array for a pattern **/
failure = new int[pat.length()];
fail(pat);
/** find match **/
int pos = posMatch(text, pat);
if (pos == -1)
System.out.println("\nNo match found");
else
System.out.println("\nMatch found at index "+ pos);
}
/** Failure function for a pattern **/
private void fail(String pat)
{
int n = pat.length();
failure[0] = -1;
for (int j = 1; j < n; j++)
{
int i = failure[j - 1];
while ((pat.charAt(j) != pat.charAt(i + 1)) && i >= 0)
i = failure[i];
if (pat.charAt(j) == pat.charAt(i + 1))
failure[j] = i + 1;
else
failure[j] = -1;
}
}
/** Function to find match for a pattern **/
private int posMatch(String text, String pat)
{
int i = 0, j = 0;
int lens = text.length();
int lenp = pat.length();
while (i < lens && j < lenp)
{
if (text.charAt(i) == pat.charAt(j))
{
i++;
j++;
}
else if (j == 0)
i++;
else
j = failure[j - 1] + 1;
}
return ((j == lenp) ? (i - lenp) : -1);
}
/** Main Function **/
public static void main(String[] args) throws IOException
{
//i think its here were i get the input
BufferedReader br = new BufferedReader(new InputStreamReader(System.in) ));
System.out.println("Knuth Morris Pratt Test\n");
System.out.println("\nEnter Text: ");
String text = br.readLine();
System.out.print("\nEnter Pattern");
String pattern = br.readLine();
double starttime = System.nanoTime();
Knuth1 kmp = new Knuth1(text, pattern);
double endtime = System.nanoTime();
double executiontime = (endtime - starttime )/1000000000;
// System.out.printf("%.4f","Execution Time = "+ executiontime + " Seconds");
System.out.print("Execution Time = ");
System.out.format("%.4f", executiontime);
System.out.print(" Seconds");
// System.out.println(starttime);
// System.out.println(endtime);
//I love programming with JAVA and Php. It’s fun and interesting.
}
}
this code will check an input strings and pick out the unique word and the try to also display the execution time for the program... what i really want now is to make sure the execution time remain the same when i input the same input.
If you don't care about reusing the timed functionality, use System.nanoTime()
long before;
long after;
// Get time before
before = System.nanoTime();
// Code you want to execute here
// Get time after
after = System.nanoTime();
// Calculate duration
long diff = after - before;
You can encapsulate any code you want into a Runnable or using any of Java 8's new Predicate or Function interfaces which are very similar. You can put the code that you want to run in a lambda expression (like an anonymous function) and pass it to a static method that calculates in nanoseconds the amount of time it takes the runnable object to execute.
This is more code than what is necessary, but it reusable in that you don't have to keep writing start = System.currentTimeMillis() or System.nanoTime()and doing arithmetic every time you want to time something. You can put this function in your static library and use it whenever you want.
/**
* Times a runnable. Note, there
* is probably overhead associated with
* creating a Runnable object, but if
* assume that's constant for all Runnable
* objects, then we can simply ignore it
*
* #param runnable Runnable to run
* #return Number of nanoseconds it took to run the Runnable
*/
public static long timedAction(Runnable runnable) {
long before;
long after;
before = System.nanoTime();
runnable.run();
after = System.nanoTime();
return after - before;
}
This is how I use this code block:
public static void main(String[] args) throws Exception {
final int numKeys = 1000000;
// Builds an AVL tree
Runnable snippet = () -> {
Tree<Integer, Object> avl = new AVLTree<>();
for (int i = 0; i < numKeys; i++) {
avl.insert(i, i);
}
};
long nanoSecond = Util.timedAction(snippet);
double seconds = Mathematics.nanosecondsToSeconds(nanoSecond);
System.out.println(seconds);
}
Output:
0.493316448
I need to know how frequency different events occur. For example how many HTTP requests have occurred in the last 15 minutes. Because there can be a large count of events (millions) this must be use a limited amount of memory.
It there any util class in Java that can do this?
How can I implement this self in Java?
Theoretical usage code can look like:
FrequencyCounter counter = new FrequencyCounter( 15, TimeUnit.Minutes );
...
counter.add();
...
int count = counter.getCount();
Edit: It must be a real time value which can changed thousand times the minute and will be query thousands times the minute. That a database or file based solution are not possible.
Here is my implementation of such a counter. The memory usage with the default precision is fewer as 100 bytes. The memory usage is independent of the event count.
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
* A counter that counts events within the past time interval. All events that occurred before this interval will be
* removed from the counter.
*/
public class FrequencyCounter {
private final long monitoringInterval;
private final int[] details;
private final AtomicInteger currentCount = new AtomicInteger();
private long startInterval;
private int total;
/**
* Create a new instance of the counter for the given interval.
*
* #param interval the time to monitor/count the events.
* #param unit the time unit of the {#code interval} argument
*/
FrequencyCounter( long interval, TimeUnit unit ) {
this( interval, unit, 16 );
}
/**
* Create a new instance of the counter for the given interval.
*
* #param interval the time to monitor/count the events.
* #param unit the time unit of the {#code interval} argument
* #param precision the count of time slices for the for the measurement
*/
FrequencyCounter( long interval, TimeUnit unit, int precision ) {
monitoringInterval = unit.toMillis( interval );
if( monitoringInterval <= 0 ) {
throw new IllegalArgumentException( "Interval mus be a positive value:" + interval );
}
details = new int[precision];
startInterval = System.currentTimeMillis() - monitoringInterval;
}
/**
* Count a single event.
*/
public void increment() {
checkInterval( System.currentTimeMillis() );
currentCount.incrementAndGet();
}
/**
* Get the current value of the counter.
*
* #return the counter value
*/
public int getCount() {
long currentTime = System.currentTimeMillis();
checkInterval( currentTime );
long diff = currentTime - startInterval - monitoringInterval;
double partFactor = (diff * details.length / (double)monitoringInterval);
int part = (int)(details[0] * partFactor);
return total + currentCount.get() - part;
}
/**
* Check the interval of the detail counters and move the interval if needed.
*
* #param time the current time
*/
private void checkInterval( final long time ) {
if( (time - startInterval - monitoringInterval) > monitoringInterval / details.length ) {
synchronized( details ) {
long detailInterval = monitoringInterval / details.length;
while( (time - startInterval - monitoringInterval) > detailInterval ) {
int currentValue = currentCount.getAndSet( 0 );
if( (total | currentValue) == 0 ) {
// for the case that the counter was not used for a long time
startInterval = time - monitoringInterval;
return;
}
int size = details.length - 1;
total += currentValue - details[0];
System.arraycopy( details, 1, details, 0, size );
details[size] = currentValue;
startInterval += detailInterval;
}
}
}
}
}
The best way I can think to implement this is using another "time counting" thread.
If you're concerned about the amount of memory, you can add a threshold for the size of eventsCounter (Integer.MAX_VALUE seems like the natural choice).
Here's an example for an implementation, that is also thread-safe:
public class FrequencyCounter {
private AtomicInteger eventsCounter = new AtomicInteger(0);
private int timeCounter;
private boolean active;
public FrequencyCounter(int timeInSeconds) {
timeCounter = timeInSeconds;
active = true;
}
// Call this method whenever an interesting event occurs
public int add() {
if(active) {
int current;
do {
current = eventsCounter.get();
} while (eventsCounter.compareAndSet(current, current + 1));
return current + 1;
}
else return -1;
}
// Get current number of events
public int getCount() {
return eventsCounter.get();
}
// Start the FrequencyCounter
public void run() {
Thread timer = new Thread(() -> {
while(timeCounter > 0) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
timeCounter --;
}
active = false;
});
timer.start();
}
}
How about a scheduled executor service.
class TimedValue{
int startValue;
int finishedValue;
TimedValue(int start){
startValue = start;
}
}
List<TimedValue> intervals = new CopyOnWriteArrayList<>();
//then when starting a measurement.
TimeValue value = new TimedValue();
//set the start value.
Callable<TimedValue> callable = ()->{
//performs the task.
value.setValueAtFinish(getCount());
return value;
}
ScheduledExecutorService executor = Executors.newScheduledThreadPool(2);
ScheduledFuture<TimedValue> future = executor.schedule(
callable,
TimeUnit.MINUTES,
15);
executor.schedule(()->itervals.add(
future.get(),
TimeUnit.MINUTES,
future.getDelay(TimeUnit.MINUTES
);
This is a bit of a complicated method.
I would probably just have a List<LoggedValues> and accumulate values in that list at a fixed rate. Then it could be inspected whenever you want to know an intervals.
The program simulates a customer service operation in places, e.g., call center, bank, store, airport, with customers being served by tellers. The customers arrive at random time and wait in a line until a teller is available to serve them. The waiting line is implemented with queue data structure. However im getting two minor errors 1.) my enqueue method is not applicable for the argument and 2.)cannot cast from int to customers. Here is the code. The error is in the bolded lines towards the end
import java.util.Random;
class MyQueue<E> {
private int maxSize;
private int[] queArray;
private int front;
private int rear;
public MyQueue(int s) // constructor
{
maxSize = s+1; // array is 1 cell larger
queArray = new int[maxSize]; // than requested
front = 0;
rear = -1;
}
public void enqueue(int j) // put item at rear of queue
{
if(rear == maxSize-1)
rear = -1;
queArray[++rear] = j;
}
public int dequeue() // take item from front of queue
{
int temp = queArray[front++];
if(front == maxSize)
front = 0;
return temp;
}
public int peek() // peek at front of queue
{
return queArray[front];
}
public boolean isEmpty() // true if queue is empty
{
return ( rear+1==front || (front+maxSize-1==rear) );
}
public boolean isFull() // true if queue is full
{
return ( rear+2==front || (front+maxSize-2==rear) );
}
public int size() // (assumes queue not empty)
{
if(rear >= front) // contiguous sequence
return rear-front+1;
else // broken sequence
return (maxSize-front) + (rear+1);
}
}
class Customer { int arrive; // Time point that the customer arrived. int processTime; // Time duration that the customer will need to be served.
/**
* Default constructor
*/
public Customer() {
arrive = 0;
processTime = 0;
}
/**
* Set the arrival time point of the customer.
*
* #param Time
* point
*/
public Customer(int arrivalTime) {
arrive = arrivalTime;
// We set the processing time as a random integer between 1 and 3.
processTime = (int) (Math.random() * 3 + 1);
}
/**
* #return the arrival time point of the customer.
*/
public int getArrivalTime() {
return arrive;
}
/**
* #return the processing time of the customer.
*/
public int getProcTime() {
return processTime;
}
}
public class Simulator { /** * The main method of the class. * * #param args * Command line arguments. */ public static void main(String[] args) {
if (args.length != 3) {
System.out.println("usage: " + Simulator.class.getSimpleName()
+ " qCapacity simHours customPerHour");
System.out.println("Example: " + Simulator.class.getSimpleName()
+ " 10 1 30");
System.exit(-1);
}
// maximum size of queue
int qCapacity = Integer.parseInt(args[0]);
// number of simulation hours
int simHours = Integer.parseInt(args[1]);
// average number of customers per hour
int custPerHour = Integer.parseInt(args[2]);
// Run simulation
simulation(qCapacity, simHours, custPerHour);
}
private static void simulation(int qCapacity, int simHours, int custPerHour) {
// Constant
final int MIN_PER_HR = 60;
// A queue that will hold and manage objects of type Customer.
MyQueue<Customer> line = new MyQueue<Customer>(qCapacity);
// For how many cycles should the simulation run. We assume that each
// cycle takes one minute.
int cycleLimit = MIN_PER_HR * simHours;
// The average number of customers can arrive per minute
float custPerMin = ((float) custPerHour) / MIN_PER_HR;
// The number of customers that were turned away because the line
// (queue)
// was full at the time they arrived.
int turnAways = 0;
// Number of customers that arrived.
int customers = 0;
// Number of customers that were served.
int served = 0;
// Total number of customers that entered the line (queue).
int sumLine = 0;
// Waiting time until the next customer is served.
int waitTime = 0;
// Total time that all the customers waited in the line.
int lineWait = 0;
// Simulation
for (int cycle = 0; cycle < cycleLimit; cycle++) {
float j = custPerMin;
while (j > 0) {
if (newCustomer(j)) {
if (line.isFull()) {
turnAways++;
} else {
customers++;
Customer customer = new Customer(cycle);
**line.enqueue(customer);**
}
}
j = j - 1;
}
if (waitTime <= 0 && !line.isEmpty())
{
**Customer customer = (Customer) line.dequeue();**
waitTime = customer.getProcTime();
lineWait += cycle - customer.getArrivalTime();
served++;
}
if (waitTime > 0) {
waitTime--;
}
sumLine += line.size();
}
// Print the simulation results.
if (customers > 0) {
System.out.println("\nCustomers accepted: " + customers);
System.out.println(" Customers served: " + served);
System.out.println(" Customers waiting: " + line.size());
System.out.println(" Turnaways: " + turnAways);
System.out.println("Average queue size: " + (float) sumLine
/ cycleLimit);
System.out.println(" Average wait time: " + (float) lineWait
/ served + " minutes");
} else {
System.out.println("No customers!");
}
}
private static boolean newCustomer(float j) {
if(j > 1)
return true;
else
return (j > Math.random() );
}
It looks like your problem is with these two methods:
public void enqueue(int j) // put item at rear of queue
{
if(rear == maxSize-1)
rear = -1;
queArray[++rear] = j;
}
public int dequeue() // take item from front of queue
{
int temp = queArray[front++];
if(front == maxSize)
front = 0;
return temp;
}
If you had intended on the Queue to hold anything but integers, then you'll need to change the argument type / return type to reflect that.
**line.enqueue(customer);**
// 1.) my enqueue method is not applicable for the argument
Your enqueue method takes an int argmuent, yet you're trying to pass a Customer type to it. Maybe you want something like this: line.enqueue(customer.getSomething());. I can't really tell from your code.
**Customer customer = (Customer) line.dequeue();**
// 2.)cannot cast from int to customers
(Customer) line.dequeue();. Here you're casting Customer to int-line.dequeue()
Your dequque method return am int so basically you're saying that a Customer equals and int, which is impossible unless Customer isint, which it isn't
You want this:
Customer customer = new Customer(line.dequeue)
// Customer constructor takes an int value
I've been trying to get midi volume control to work in a MidiPlayer class for a very long time now. I've searched for examples for accomplishing this here on stackoverflow and all over the Internet, but nothing I try ever seems to work. The volume stays the same! It doesn't change like I want it to.
I'm running Java 1.6.0_32 on Windows 7 professional.
Here! Have an SSCCE:
import java.io.*;
import javax.sound.midi.*;
import java.net.URL;
public class MidiSSCCE {
public static void main(String[] args)
{
// creates the midi player and sets up its sequencer and synthesizer.
MidiPlayer midiP = new MidiPlayer();
double volume = 1.0;
// loads a midi from a url into the sequencer, but doesn't start playing it yet.
midiP.load("http://www.vgmusic.com/music/computer/microsoft/windows/touhou_6_stage3_boss.mid",true);
// set the midi to loop indefinitely.
midiP.loop(-1);
// start playing the midi.
midiP.play(true);
// while loop changes the volume of the midi while it is playing.
while(true) {
midiP.setVolume(volume);
try { Thread.sleep(300); } catch(Exception e) {}
volume -= 0.1;
if(volume < 0) volume += 1.0;
}
}
}
/**
* MidiPlayer
* author: Stephen Lindberg
* Last modified: Oct 14, 2011
*
* A class that allows midi files to be loaded and played.
**/
class MidiPlayer {
private Sequence seq;
private Sequencer seqr;
private Synthesizer synth;
private Receiver receiver;
private File midiFile;
private String midiID;
private boolean loaded;
private boolean usingHardwareSoundbank;
// CONSTRUCTORS
public MidiPlayer() {
loaded = false;
try {
// obtain the sequencer and synthesizer.
seqr = MidiSystem.getSequencer();
synth = MidiSystem.getSynthesizer();
// print the user's midi device info
System.out.println("Setting up Midi Player...");
System.out.println("MidiDeviceInfo: ");
for(MidiDevice.Info info : MidiSystem.getMidiDeviceInfo()) {
System.out.println("\t" + info.getName() + ": " +info.getDescription());
}
System.out.println();
// obtain the soundbank and receiver.
Soundbank soundbank = synth.getDefaultSoundbank();
if(soundbank == null) {
receiver = MidiSystem.getReceiver();
usingHardwareSoundbank = true;
System.out.println("using hardware soundbank");
}
else {
synth.loadAllInstruments(soundbank);
receiver = synth.getReceiver();
usingHardwareSoundbank = false;
System.out.println("using default software soundbank:" + soundbank);
}
seqr.getTransmitter().setReceiver(receiver);
}
catch(Exception e) {
System.out.println("MIDI error: I just don't know what went wrong! 6_9");
}
}
// DATA METHODS
/**
* load(String fileName)
* loads a midi file into this MidiPlayer.
* Preconditions: fileName is the name of the midi file to be loaded.
* Postconditions: fileName is loaded and is ready to be played.
**/
public void load(String fileName, boolean isOnline) {
this.unload();
try {
URL midiURL;
if(isOnline) midiURL = new URL(fileName);
else midiURL = getClass().getClassLoader().getResource(fileName);
seq = MidiSystem.getSequence(midiURL);
seqr.open();
synth.open();
// load our sequence into the sequencer.
seqr.setSequence(seq);
loaded = true;
}
catch(IOException ioe) {
System.out.println("MIDI error: Problem occured while reading " + midiFile.getName() + ".");
}
catch(InvalidMidiDataException imde) {
System.out.println("MIDI error: " + midiFile.getName() + " is not a valid MIDI file or is unreadable.");
}
catch(Exception e) {
System.out.println("MIDI error: I just don't know what went wrong! 6_9");
}
}
/**
* unload()
* Unloads the current midi from the MidiPlayer and releases its resources from memory.
**/
public void unload() {
this.stop();
seqr.close();
synth.close();
midiFile = null;
loaded = false;
}
// OTHER METHODS
/**
* play(boolean reset)
* plays the currently loaded midi.
* Preconditions: reset tells our midi whether or nor to begin playing from the start of the midi file's current loop start point.
* Postconditions: If reset is true, then the loaded midi begins playing from its loop start point (default 0).
* If reset is false, then the loaded midi resumes playing from its current position.
**/
public void play(boolean reset) {
if(reset) seqr.setTickPosition(seqr.getLoopStartPoint());
seqr.start();
}
/**
* stop()
* Pauses the current midi if it was playing.
**/
public void stop() {
if(seqr.isOpen()) seqr.stop();
}
/**
* isRunning()
* Returns true if the current midi is playing. Returns false otherwise.
**/
public boolean isRunning() {
return seqr.isRunning();
}
/**
* loop(int times)
* Sets the current midi to loop from start to finish a specific number of times.
* Preconditions: times is the number of times we want our midi to loop.
* Postconditions: The current midi is set to loop times times.
* If times = -1, the current midi will be set to loop infinitely.
**/
public void loop(int times)
{
loop(times,0,-1);
}
/**
* loop(int times)
* Sets the current midi to loop from a specified start point to a specified end point a specific number of times.
* Preconditions: times is the number of times we want our midi to loop.
* start is our loop's start point in ticks.
* end is our loop's end point in ticks.
* Postconditions: The current midi is set to loop from tick start to tick end times times.
* If times = -1, the current midi will be set to loop infinitely.
**/
public void loop(int times, long start, long end) {
if(start < 0) start = 0;
if(end > seqr.getSequence().getTickLength() || end <= 0) end = seqr.getSequence().getTickLength();
if(start >= end && end != -1) start = end-1;
seqr.setLoopStartPoint(start);
seqr.setLoopEndPoint(end);
if(times == -1) seqr.setLoopCount(Sequencer.LOOP_CONTINUOUSLY);
else seqr.setLoopCount(times);
}
public void setVolume(double vol) {
System.out.println("Midi volume change request: " + vol);
try {
if(usingHardwareSoundbank) {
ShortMessage volumeMessage = new ShortMessage();
for ( int i = 0; i < 16; i++ ) {
volumeMessage.setMessage( ShortMessage.CONTROL_CHANGE, i, 7, (int)(vol*127) );
receiver.send( volumeMessage, -1 );
}
}
else {
MidiChannel[] channels = synth.getChannels();
for( int c = 0; c < channels.length; c++ ) {
if(channels[c] != null) channels[c].controlChange( 7, (int)( vol*127) );
}
}
}
catch ( Exception e ) {
e.printStackTrace();
}
}
}
I've tried examples at the following sources with no success:
http://www.java2s.com/Code/Java/Development-Class/SettingtheVolumeofPlayingMidiAudio.htm
How to controll the MIDI channel's volume
https://forums.oracle.com/forums/thread.jspa?messageID=5389030
MIDI Song with CC
http://www.codezealot.org/archives/27
http://www.exampledepot.com/egs/javax.sound.midi/Volume.html
I'm struggling with these very issues on controlling sound and i've found a code to change volume that works. Unfurtunatly i was unable to understand it, but here is something in your code that is differente from the one i've seen. Maybe it can helps you.
Try change the line
seqr = MidiSystem.getSequencer();
for
seqr = MidiSystem.getSequencer(false);
Maybe it helps you, I believe that using the "false" the sequencer will connect to the receiver, and not to the synthesizer, then when you send the message to the receiver to set volume it will work.
public void setVolume(double vol) {
System.out.println("Midi volume change request: " + vol);
try {
if(usingHardwareSoundbank) {
ShortMessage volumeMessage = new ShortMessage();
for ( int i = 0; i < 16; i++ ) {
volumeMessage.setMessage( ShortMessage.CONTROL_CHANGE, i, 7, (int)(vol*127) );
receiver.send( volumeMessage, -1 );
}
}
else {
MidiChannel[] channels = synth.getChannels();
for( int c = 0; c < channels.length; c++ ) {
if(channels[c] != null) channels[c].controlChange( 7, (int)( vol*127) );
}
}
// Very important!
seqr.setSequence(seq);
}
catch ( Exception e ) {
e.printStackTrace();
}
}
I need a component/class that throttles execution of some method to maximum M calls in N seconds (or ms or nanos, does not matter).
In other words I need to make sure that my method is executed no more than M times in a sliding window of N seconds.
If you don't know existing class feel free to post your solutions/ideas how you would implement this.
I'd use a ring buffer of timestamps with a fixed size of M. Each time the method is called, you check the oldest entry, and if it's less than N seconds in the past, you execute and add another entry, otherwise you sleep for the time difference.
What worked out of the box for me was Google Guava RateLimiter.
// Allow one request per second
private RateLimiter throttle = RateLimiter.create(1.0);
private void someMethod() {
throttle.acquire();
// Do something
}
In concrete terms, you should be able to implement this with a DelayQueue. Initialize the queue with M Delayed instances with their delay initially set to zero. As requests to the method come in, take a token, which causes the method to block until the throttling requirement has been met. When a token has been taken, add a new token to the queue with a delay of N.
Read up on the Token bucket algorithm. Basically, you have a bucket with tokens in it. Every time you execute the method, you take a token. If there are no more tokens, you block until you get one. Meanwhile, there is some external actor that replenishes the tokens at a fixed interval.
I'm not aware of a library to do this (or anything similar). You could write this logic into your code or use AspectJ to add the behavior.
If you need a Java based sliding window rate limiter that will operate across a distributed system you might want to take a look at the https://github.com/mokies/ratelimitj project.
A Redis backed configuration, to limit requests by IP to 50 per minute would look like this:
import com.lambdaworks.redis.RedisClient;
import es.moki.ratelimitj.core.LimitRule;
RedisClient client = RedisClient.create("redis://localhost");
Set<LimitRule> rules = Collections.singleton(LimitRule.of(1, TimeUnit.MINUTES, 50)); // 50 request per minute, per key
RedisRateLimit requestRateLimiter = new RedisRateLimit(client, rules);
boolean overLimit = requestRateLimiter.overLimit("ip:127.0.0.2");
See https://github.com/mokies/ratelimitj/tree/master/ratelimitj-redis fore further details on Redis configuration.
This depends in the application.
Imagine the case in which multiple threads want a token to do some globally rate-limited action with no burst allowed (i.e. you want to limit 10 actions per 10 seconds but you don't want 10 actions to happen in the first second and then remain 9 seconds stopped).
The DelayedQueue has a disadvantage: the order at which threads request tokens might not be the order at which they get their request fulfilled. If multiple threads are blocked waiting for a token, it is not clear which one will take the next available token. You could even have threads waiting forever, in my point of view.
One solution is to have a minimum interval of time between two consecutive actions, and take actions in the same order as they were requested.
Here is an implementation:
public class LeakyBucket {
protected float maxRate;
protected long minTime;
//holds time of last action (past or future!)
protected long lastSchedAction = System.currentTimeMillis();
public LeakyBucket(float maxRate) throws Exception {
if(maxRate <= 0.0f) {
throw new Exception("Invalid rate");
}
this.maxRate = maxRate;
this.minTime = (long)(1000.0f / maxRate);
}
public void consume() throws InterruptedException {
long curTime = System.currentTimeMillis();
long timeLeft;
//calculate when can we do the action
synchronized(this) {
timeLeft = lastSchedAction + minTime - curTime;
if(timeLeft > 0) {
lastSchedAction += minTime;
}
else {
lastSchedAction = curTime;
}
}
//If needed, wait for our time
if(timeLeft <= 0) {
return;
}
else {
Thread.sleep(timeLeft);
}
}
}
My implementation below can handle arbitrary request time precision, it has O(1) time complexity for each request, does not require any additional buffer, e.g. O(1) space complexity, in addition it does not require background thread to release token, instead tokens are released according to time passed since last request.
class RateLimiter {
int limit;
double available;
long interval;
long lastTimeStamp;
RateLimiter(int limit, long interval) {
this.limit = limit;
this.interval = interval;
available = 0;
lastTimeStamp = System.currentTimeMillis();
}
synchronized boolean canAdd() {
long now = System.currentTimeMillis();
// more token are released since last request
available += (now-lastTimeStamp)*1.0/interval*limit;
if (available>limit)
available = limit;
lastTimeStamp = now;
if (available<1)
return false;
else {
available--;
return true;
}
}
}
Although it's not what you asked, ThreadPoolExecutor, which is designed to cap to M simultaneous requests instead of M requests in N seconds, could also be useful.
I have implemented a simple throttling algorithm.Try this link,
http://krishnaprasadas.blogspot.in/2012/05/throttling-algorithm.html
A brief about the Algorithm,
This algorithm utilizes the capability of Java Delayed Queue.
Create a delayed object with the expected delay (here 1000/M for millisecond TimeUnit).
Put the same object into the delayed queue which will intern provides the moving window for us.
Then before each method call take the object form the queue, take is a blocking call which will return only after the specified delay, and after the method call don't forget to put the object into the queue with updated time(here current milliseconds).
Here we can also have multiple delayed objects with different delay. This approach will also provide high throughput.
Try to use this simple approach:
public class SimpleThrottler {
private static final int T = 1; // min
private static final int N = 345;
private Lock lock = new ReentrantLock();
private Condition newFrame = lock.newCondition();
private volatile boolean currentFrame = true;
public SimpleThrottler() {
handleForGate();
}
/**
* Payload
*/
private void job() {
try {
Thread.sleep(Math.abs(ThreadLocalRandom.current().nextLong(12, 98)));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.err.print(" J. ");
}
public void doJob() throws InterruptedException {
lock.lock();
try {
while (true) {
int count = 0;
while (count < N && currentFrame) {
job();
count++;
}
newFrame.await();
currentFrame = true;
}
} finally {
lock.unlock();
}
}
public void handleForGate() {
Thread handler = new Thread(() -> {
while (true) {
try {
Thread.sleep(1 * 900);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
currentFrame = false;
lock.lock();
try {
newFrame.signal();
} finally {
lock.unlock();
}
}
}
});
handler.start();
}
}
Apache Camel also supports comes with Throttler mechanism as follows:
from("seda:a").throttle(100).asyncDelayed().to("seda:b");
This is an update to the LeakyBucket code above.
This works for a more that 1000 requests per sec.
import lombok.SneakyThrows;
import java.util.concurrent.TimeUnit;
class LeakyBucket {
private long minTimeNano; // sec / billion
private long sched = System.nanoTime();
/**
* Create a rate limiter using the leakybucket alg.
* #param perSec the number of requests per second
*/
public LeakyBucket(double perSec) {
if (perSec <= 0.0) {
throw new RuntimeException("Invalid rate " + perSec);
}
this.minTimeNano = (long) (1_000_000_000.0 / perSec);
}
#SneakyThrows public void consume() {
long curr = System.nanoTime();
long timeLeft;
synchronized (this) {
timeLeft = sched - curr + minTimeNano;
sched += minTimeNano;
}
if (timeLeft <= minTimeNano) {
return;
}
TimeUnit.NANOSECONDS.sleep(timeLeft);
}
}
and the unittest for above:
import com.google.common.base.Stopwatch;
import org.junit.Ignore;
import org.junit.Test;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;
public class LeakyBucketTest {
#Test #Ignore public void t() {
double numberPerSec = 10000;
LeakyBucket b = new LeakyBucket(numberPerSec);
Stopwatch w = Stopwatch.createStarted();
IntStream.range(0, (int) (numberPerSec * 5)).parallel().forEach(
x -> b.consume());
System.out.printf("%,d ms%n", w.elapsed(TimeUnit.MILLISECONDS));
}
}
Here is a little advanced version of simple rate limiter
/**
* Simple request limiter based on Thread.sleep method.
* Create limiter instance via {#link #create(float)} and call {#link #consume()} before making any request.
* If the limit is exceeded cosume method locks and waits for current call rate to fall down below the limit
*/
public class RequestRateLimiter {
private long minTime;
private long lastSchedAction;
private double avgSpent = 0;
ArrayList<RatePeriod> periods;
#AllArgsConstructor
public static class RatePeriod{
#Getter
private LocalTime start;
#Getter
private LocalTime end;
#Getter
private float maxRate;
}
/**
* Create request limiter with maxRate - maximum number of requests per second
* #param maxRate - maximum number of requests per second
* #return
*/
public static RequestRateLimiter create(float maxRate){
return new RequestRateLimiter(Arrays.asList( new RatePeriod(LocalTime.of(0,0,0),
LocalTime.of(23,59,59), maxRate)));
}
/**
* Create request limiter with ratePeriods calendar - maximum number of requests per second in every period
* #param ratePeriods - rate calendar
* #return
*/
public static RequestRateLimiter create(List<RatePeriod> ratePeriods){
return new RequestRateLimiter(ratePeriods);
}
private void checkArgs(List<RatePeriod> ratePeriods){
for (RatePeriod rp: ratePeriods ){
if ( null == rp || rp.maxRate <= 0.0f || null == rp.start || null == rp.end )
throw new IllegalArgumentException("list contains null or rate is less then zero or period is zero length");
}
}
private float getCurrentRate(){
LocalTime now = LocalTime.now();
for (RatePeriod rp: periods){
if ( now.isAfter( rp.start ) && now.isBefore( rp.end ) )
return rp.maxRate;
}
return Float.MAX_VALUE;
}
private RequestRateLimiter(List<RatePeriod> ratePeriods){
checkArgs(ratePeriods);
periods = new ArrayList<>(ratePeriods.size());
periods.addAll(ratePeriods);
this.minTime = (long)(1000.0f / getCurrentRate());
this.lastSchedAction = System.currentTimeMillis() - minTime;
}
/**
* Call this method before making actual request.
* Method call locks until current rate falls down below the limit
* #throws InterruptedException
*/
public void consume() throws InterruptedException {
long timeLeft;
synchronized(this) {
long curTime = System.currentTimeMillis();
minTime = (long)(1000.0f / getCurrentRate());
timeLeft = lastSchedAction + minTime - curTime;
long timeSpent = curTime - lastSchedAction + timeLeft;
avgSpent = (avgSpent + timeSpent) / 2;
if(timeLeft <= 0) {
lastSchedAction = curTime;
return;
}
lastSchedAction = curTime + timeLeft;
}
Thread.sleep(timeLeft);
}
public synchronized float getCuRate(){
return (float) ( 1000d / avgSpent);
}
}
And unit tests
import org.junit.Assert;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class RequestRateLimiterTest {
#Test(expected = IllegalArgumentException.class)
public void checkSingleThreadZeroRate(){
// Zero rate
RequestRateLimiter limiter = RequestRateLimiter.create(0);
try {
limiter.consume();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
#Test
public void checkSingleThreadUnlimitedRate(){
// Unlimited
RequestRateLimiter limiter = RequestRateLimiter.create(Float.MAX_VALUE);
long started = System.currentTimeMillis();
for ( int i = 0; i < 1000; i++ ){
try {
limiter.consume();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
long ended = System.currentTimeMillis();
System.out.println( "Current rate:" + limiter.getCurRate() );
Assert.assertTrue( ((ended - started) < 1000));
}
#Test
public void rcheckSingleThreadRate(){
// 3 request per minute
RequestRateLimiter limiter = RequestRateLimiter.create(3f/60f);
long started = System.currentTimeMillis();
for ( int i = 0; i < 3; i++ ){
try {
limiter.consume();
Thread.sleep(20000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
long ended = System.currentTimeMillis();
System.out.println( "Current rate:" + limiter.getCurRate() );
Assert.assertTrue( ((ended - started) >= 60000 ) & ((ended - started) < 61000));
}
#Test
public void checkSingleThreadRateLimit(){
// 100 request per second
RequestRateLimiter limiter = RequestRateLimiter.create(100);
long started = System.currentTimeMillis();
for ( int i = 0; i < 1000; i++ ){
try {
limiter.consume();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
long ended = System.currentTimeMillis();
System.out.println( "Current rate:" + limiter.getCurRate() );
Assert.assertTrue( (ended - started) >= ( 10000 - 100 ));
}
#Test
public void checkMultiThreadedRateLimit(){
// 100 request per second
RequestRateLimiter limiter = RequestRateLimiter.create(100);
long started = System.currentTimeMillis();
List<Future<?>> tasks = new ArrayList<>(10);
ExecutorService exec = Executors.newFixedThreadPool(10);
for ( int i = 0; i < 10; i++ ) {
tasks.add( exec.submit(() -> {
for (int i1 = 0; i1 < 100; i1++) {
try {
limiter.consume();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}) );
}
tasks.stream().forEach( future -> {
try {
future.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
});
long ended = System.currentTimeMillis();
System.out.println( "Current rate:" + limiter.getCurRate() );
Assert.assertTrue( (ended - started) >= ( 10000 - 100 ) );
}
#Test
public void checkMultiThreaded32RateLimit(){
// 0,2 request per second
RequestRateLimiter limiter = RequestRateLimiter.create(0.2f);
long started = System.currentTimeMillis();
List<Future<?>> tasks = new ArrayList<>(8);
ExecutorService exec = Executors.newFixedThreadPool(8);
for ( int i = 0; i < 8; i++ ) {
tasks.add( exec.submit(() -> {
for (int i1 = 0; i1 < 2; i1++) {
try {
limiter.consume();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}) );
}
tasks.stream().forEach( future -> {
try {
future.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
});
long ended = System.currentTimeMillis();
System.out.println( "Current rate:" + limiter.getCurRate() );
Assert.assertTrue( (ended - started) >= ( 10000 - 100 ) );
}
#Test
public void checkMultiThreadedRateLimitDynamicRate(){
// 100 request per second
RequestRateLimiter limiter = RequestRateLimiter.create(100);
long started = System.currentTimeMillis();
List<Future<?>> tasks = new ArrayList<>(10);
ExecutorService exec = Executors.newFixedThreadPool(10);
for ( int i = 0; i < 10; i++ ) {
tasks.add( exec.submit(() -> {
Random r = new Random();
for (int i1 = 0; i1 < 100; i1++) {
try {
limiter.consume();
Thread.sleep(r.nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}) );
}
tasks.stream().forEach( future -> {
try {
future.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
});
long ended = System.currentTimeMillis();
System.out.println( "Current rate:" + limiter.getCurRate() );
Assert.assertTrue( (ended - started) >= ( 10000 - 100 ) );
}
}
My solution: A simple util method, you can modify it to create a wrapper class.
public static Runnable throttle (Runnable realRunner, long delay) {
Runnable throttleRunner = new Runnable() {
// whether is waiting to run
private boolean _isWaiting = false;
// target time to run realRunner
private long _timeToRun;
// specified delay time to wait
private long _delay = delay;
// Runnable that has the real task to run
private Runnable _realRunner = realRunner;
#Override
public void run() {
// current time
long now;
synchronized (this) {
// another thread is waiting, skip
if (_isWaiting) return;
now = System.currentTimeMillis();
// update time to run
// do not update it each time since
// you do not want to postpone it unlimited
_timeToRun = now+_delay;
// set waiting status
_isWaiting = true;
}
try {
Thread.sleep(_timeToRun-now);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// clear waiting status before run
_isWaiting = false;
// do the real task
_realRunner.run();
}
}};
return throttleRunner;
}
Take from JAVA Thread Debounce and Throttle
Here is a rate limiter implementation based on #tonywl (and somewhat relates to Duarte Meneses's leaky bucket). The idea is the same - use a "token pool" to allow both rate limiting and bursting (make multiple calls in a short time after idling for a bit).
This implementation offers two main differences:
Lock-less concurrent access using atomic operations.
Instead of blocking a request, calculate a delay needed to enforce the rate limit and offers that as the response, allow the caller to enforce the delay - this will work better with asynchronous programming that you can find in modern networking frameworks.
The full implementation with documentation can be found in this Github Gist, which is where I'll also post updates, but here's the gist of it:
import java.util.concurrent.atomic.AtomicLong;
public class RateLimiter {
private final static long TOKEN_SIZE = 1_000_000 /* tockins per token */;
private final double tokenRate; // measured in tokens per ms
private final double tockinRate; // measured in tockins per ms
private final long tockinsLimit;
private AtomicLong available;
private AtomicLong lastTimeStamp;
public RateLimiter(int prefill, int limit, int fill, long interval) {
this.tokenRate = (double)fill / interval;
this.tockinsLimit = TOKEN_SIZE * limit;
this.tockinRate = tokenRate * TOKEN_SIZE;
this.lastTimeStamp = new AtomicLong(System.nanoTime());
this.available = new AtomicLong(Math.max(prefill, limit) * TOKEN_SIZE);
}
public boolean allowRequest() {
return whenNextAllowed(1, false) == 0;
}
public boolean allowRequest(int cost) {
return whenNextAllowed(cost, false) == 0;
}
public long whenNextAllowed(boolean alwaysConsume) {
return whenNextAllowed(1, alwaysConsume);
}
/**
* Check when will the next call be allowed, according to the specified rate.
* The value returned is in milliseconds. If the result is 0 - or if {#code alwaysConsume} was
* specified then the RateLimiter has recorded that the call has been allowed.
* #param cost How costly is the requested action. The base rate is 1 token per request,
* but the client can declare a more costly action that consumes more tokens.
* #param alwaysConsume if set to {#code true} this method assumes that the caller will delay
* the action that is rate limited but will perform it without checking again - so it will
* consume the specified number of tokens as if the action has gone through. This means that
* the pool can get into a deficit, which will further delay additional actions.
* #return how long before this request should be let through.
*/
public long whenNextAllowed(int cost, boolean alwaysConsume) {
var now = System.nanoTime();
var last = lastTimeStamp.getAndSet(now);
// calculate how many tockins we got since last call
// if the previous call was less than a microsecond ago, we still accumulate at least
// one tockin, which is probably more than we should, but this is too small to matter - right?
var add = (long)Math.ceil(tokenRate * (now - last));
var nowAvailable = available.addAndGet(add);
while (nowAvailable > tockinsLimit) {
available.compareAndSet(nowAvailable, tockinsLimit);
nowAvailable = available.get();
}
// answer the question
var toWait = (long)Math.ceil(Math.max(0, (TOKEN_SIZE - nowAvailable) / tockinRate));
if (alwaysConsume || toWait == 0) // the caller will let the request go through, so consume a token now
available.addAndGet(-TOKEN_SIZE);
return toWait;
}
}