package com.interview.multithreaded;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicLongArray;
/**
* Date 03/03/2015
* @author tusroy
*
* Develop a software to count number of events in last 5 mins. You have to support two apis
* 1) addEvent() -> It means increment event by 1
* 2) getTotalEvents() -> Return total number of events in last 5 mins
*
* Program should support millions of events every minute and should also provide multi-threading support
*
* This class might not have 100% accuracy as far as events in last 5 mins are concerned.
* Since we are using circular queue last second information may not be very accurate.
*
* Solution:
* Keep atomiclong of 300 in array. PositionUpdater updates position every second.
* @Threadsafe
*/
public class RealTimeCounter {
private final static int GRANULARITY = 300;
private AtomicLongArray counter = new AtomicLongArray(GRANULARITY);
private volatile int pos = 0;
private RealTimeCounter(){
PositionUpdater positionUpdater = new PositionUpdater(this);
positionUpdater.start();
}
private static volatile RealTimeCounter INSTANCE;
public static RealTimeCounter getInstance(){
if(INSTANCE == null){
synchronized (RealTimeCounter.class) {
if(INSTANCE == null){
INSTANCE = new RealTimeCounter();
}
}
}
return INSTANCE;
}
public long getTotalEvents(){
int total = 0;
for(int i=0; i < GRANULARITY; i++){
total += counter.get(i);
}
return total;
}
public void addEvent(){
counter.getAndIncrement(pos);
}
void incrementPosition(){
//first reset the value to 0 at next counter location.
counter.set((pos + 1)%GRANULARITY, 0);
pos = (pos + 1)%GRANULARITY;
}
public static void main(String args[]){
ExecutorService executor = Executors.newFixedThreadPool(10);
RealTimeCounter realTimeCounter = new RealTimeCounter();
final Random random = new Random();
final int TOTAL_EVENTS = 10000;
CountDownLatch countDownLatch = new CountDownLatch(TOTAL_EVENTS);
for(int i=0; i < TOTAL_EVENTS; i++){
executor.execute(() -> {
realTimeCounter.addEvent();
try {
Thread.sleep(random.nextInt(10));
} catch (Exception e) {
e.printStackTrace();
}
countDownLatch.countDown();
}
);
}
try{
countDownLatch.await();
}catch(Exception e){
}
System.out.println(realTimeCounter.getTotalEvents());
executor.shutdownNow();
}
}
class PositionUpdater extends TimerTask{
private final RealTimeCounter realTimeCounter;
private final Timer timer = new Timer(true);
private static final int DELAY = 1000;
PositionUpdater(RealTimeCounter realTimeCounter) {
this.realTimeCounter = realTimeCounter;
}
public void start(){
timer.schedule(this, DELAY);
}
@Override
public void run() {
realTimeCounter.incrementPosition();
}
}