实现支持老化的集合的最佳方式

时间:2013-07-29 16:55:37

标签: java performance

我正在寻找有关使用Java处理以下问题集的最佳方法的想法。在这种情况下,最好强调表现。

假设我们有多家银行分行。每个分支都有一个带有传感器的保险库,只要门的状态发生变化,该传感器就会将消息(在这种情况下使用UDP)发送回服务器。

在服务器上有一些类型的集合,用于存储从传感器发送的每条消息。代码在获取消息时插入事件(“门打开分支1”)。当传感器随后发送跟进消息(“门关闭分支1”)时,消息将从集合中删除。每条消息都与时间戳一起存储在集合中。

我们想要的是在消息在集合中的时间超过指定的经过时间(例如2分钟)时调用一个方法。在这个用例中,“金库门已打开超过2分钟,打电话给警察”。

最明显的解决方案是一个休眠2分钟的线程,唤醒并运行检查时间戳的集合。看似简单但不确定它是否是处理问题的更有效方法。它还需要并发收集,这不是问题。

在现实世界中,集合需要处理大约50K或更少的消息。

关于如何处理此问题的任何其他想法?在这种情况下,是否有任何可以提供帮助的课程。

谢谢

4 个答案:

答案 0 :(得分:4)

您可以使用一个或多个DelayQueue来完成此任务。

public static class BankCheck implements Delayed {
    private static final long delay = TimeUnit.NANOSECONDS.convert(2, TimeUnit.MINUTES);
    private final long created = System.nanoTime();
    private final Bank bank;

    public BankCheck(Bank bank) {
        this.bank = bank;
    }

    public Bank getBank() {
        return bank;
    }

    @Override
    public int compareTo(Delayed o) {
        if (o instanceof BankCheck) {
            BankCheck bc = (BankCheck) o;
            if (created == bc.created) {
                return 0;
            } else {
                return created < bc.created ? -1 : 1;
            }
        }

        long d = getDelay(TimeUnit.NANOSECONDS) - o.getDelay(TimeUnit.NANOSECONDS);
        if (d == 0) { return 0; }
        return d < 0 ? -1 : 1;
    }

    @Override
    public long getDelay(TimeUnit unit) {
        long elapsed = System.nanoTime() - created;
        long remaining = delay - elapsed;
        return remaining > 0 ? unit.convert(remaining, TimeUnit.NANOSECONDS) : 0;
    }
}

BlockingQueue<BankCheck> queue = new DelayQueue<>();

Thread messageReceiver = new Thread(new Runnable() {
    @Override
    public void run() {
        for(;;) {
            if(messageReceived) {
                queue.add(new BankCheck(getBankFromLastMessage()));
            }
        }
    }
}).start();
Thread bankChecker = new Thread(new Runnable() {
    @Override
    public void run() {
        try {
            for(;;) {
                Bank b = queue.take().getBank();
                if(!hasBeenClosed(b) {
                    alertAuthorities(b);
                }
            }
        } catch(InterruptedException e) {
            // handle exception
        }
    }
}).start();

答案 1 :(得分:0)

对于每个“门打开”消息,使用ScheduledExecutorService创建一个延迟2分钟的任务。如果在超时之前收到“关闭门”消息,则取消该任务。

答案 2 :(得分:0)

这是使用LinkedHashSet的解决方案,该解决方案应该很快,至少在理论界限方面:

LinkedHashSet<Bank> openSet;

void onOpen(Bank b) {
    openSet.add(b); // O(1)
}

void onClose(Bank b) {
    openSet.remove(b); // O(1)
}

void onceInAWhile() {
    Time alertTime = getAlertTime(); // If a door has been opened longer than this time, alarm.

    Iterator<Bank> it = openSet.iterator();
    while(it.hasNext()) {
        Bank b = it.next();

        if(b.getTime() < alertTime) {
            NickyMinaj.poundTheAlarm(b);
        } else {
            // The set is FIFO, so you can break early
            // You'll never examine more than N+1 banks, where N is the number that have to sound the alarm (so that's pretty optimal).
            break; 
        }
    }
}

答案 3 :(得分:0)

我在我的代码中使用这样的数据结构,我称之为EvictionList。每隔X秒,它将从列表中删除在特定时间内未被触摸的元素。我用它作为心跳监视器。我有大约100个元素,性能从来都不是问题。这里有一些伪代码,因为真正的代码第二次无法访问:

public class EvictionList<T> implements Runnable
{
    private evictionTime = 30; // seconds before eviction
    private ConcurrentHashMap<T, Long> list = new ConcurrentHashMap();

    public EvictionList()
    {
        new Thread(this).start();
    }

    public void run()
    {
        while (keepRunning)
        {
            Thread.sleep(5000); // sleep 5 seconds
            List<T> l = evict();
            // do your actions here
        }
    }

    public void add(T t)
    {
        list.put(t, System.currentMillis());
    }

    public void touch(T t)
    {
        list.put(t, System.currentMillis());
    }

    public List<T> evict()
    {
       List<T> evicted = new ArrayList();
       for (Iterator<T> i=list.keySet().iterator(); i.hasNext();)
       {
           T t = i.next();
           if (list.get(t) < System.currentMillis() - evictionTime)
               evicted.add(t);
       }
       return evict();
    }

}