0

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

假设我们有许多银行分行。每个分支都有一个带有传感器的保险库,每当门的状态发生变化时,该传感器就会将消息(在本例中使用 UDP)发送回服务器。

在服务器上,有一个存储从传感器发送的每条消息的某种集合。代码在收到消息时插入一个事件(“door open branch 1”)。当传感器随后发送后续消息(“门已关闭分支 1”)时,该消息将从集合中删除。每条消息都与时间戳一起存储在集合中。

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

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

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

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

谢谢

4

4 回答 4

4

您可以使用一个或多个DelayQueues 来完成此操作。

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();
于 2013-07-29T17:34:48.763 回答
0

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

于 2013-07-29T17:12:45.623 回答
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; 
        }
    }
}
于 2013-07-29T17:13:22.280 回答
0

我在我的代码中使用了这样的数据结构,我称之为 EvictionList。每隔 X 秒,它会从 List 中删除在特定时间内未触及的元素。我用它作为心跳监视器。我有大约 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();
    }

}
于 2013-07-29T17:14:22.233 回答