【问题标题】:Monitor HashMap update using thread使用线程监控 HashMap 更新
【发布时间】:2014-07-04 22:50:14
【问题描述】:

使用 Java 线程监控 Java 数据结构更新的最佳方法是什么?我将通过实现一个 Runnable 接口来创建一个线程。该线程需要每 10 分钟休眠和唤醒一次,并检查 HashMap 是否已更新。在 Java 中执行此操作的最佳方法是什么? 任何帮助,非常感谢

【问题讨论】:

  • 这看起来非常像XY Problem
  • 听起来有点生硬,也许您可​​以考虑为 HashMap 提供某种包装类,它会在地图更新时触发 changeEvent。然后任何可能正在监听该事件的东西都可以做出相应的反应。
  • @Ordous - 我相信 is 这个词会比 看起来非常像 更好:P
  • 所以,我基本上拥有的是一个每隔一段时间更新一次的 HashMap(或 ConcurrentHashmap)。我希望能够创建一个每 10 分钟唤醒一次并检查 HashMap 是否有新条目的线程。

标签: java multithreading concurrency executorservice


【解决方案1】:

试试:

ScheduledExecutorService daemon = Executors.newScheduledThreadPool(1);
daemon.scheduleWithFixedDelay(new MyRunnable(myMapToCheck), 
                              10, 10, TimeUnit.MINUTE);
//...
private static class MyRunnable implements Runnable {
    private final Map<K, V> lastState;
    private final Map<K, V> map;

    public MyRunnable(Map<K, V> map) {
        this.map = map;
        this.lastState = new HashMap<>(map);
    }

    @Override
    public void run() {
        // copy to avoid changes before updating lastState
        Map<K, V> currentState = new HashMap<>(map);
        if(currentState.equals(lastState)) {
            System.our.println("No change");
        } else {
            System.our.println("Change!");
        }
        lastState.clear();
        lastState.putAll(currentState);
    }
}

【讨论】:

  • 如何保证所做的更改(添加的元素)将在此线程中可见?这将如何确保发生在之前?
【解决方案2】:

您似乎想要一个 TimerTask 来检查地图是否每 10 分钟更新一次。

private Map map = new HashMap();
private Map lastMapState = new HashMap(map);
private boolean updated = false;

//....

private void startCheckingUpdates() {

    // Timers run their tasks in a separate Thread
    new Timer().schedule(new TimerTask() {
        @Override
        public void run() {
        checkUpdated();
        }
    }, 600_000, 600_000);
}

//....

private boolean checkUpdated() {
    updated = !map.equals(lastMapState);
    lastMapState.clear();
    lastMapState.putAll(map);
    return updated;
}

【讨论】:

    【解决方案3】:

    使用默认的 HashMap 实现并不容易:您必须保留 HashMap 的副本,因为线程最后一次看到它,然后在最后看到的状态和当前状态之间进行深度比较.进一步增加的问题是,该方法无法捕捉到地图发生变化然后在线程唤醒之前又变回来的情况。

    我要做的是继承 HashMap 并添加一个getLastModified 方法。覆盖每个修改器方法(put、remove 等)并设置 lastModified 时间,然后调用 super 实现。然后,您的线程可以简单地检查您的新 ChangeTrackingHashMap 的修改时间,以查看自上次检查以来它是否已更改。

    【讨论】:

    • 他仍然需要synchronize 访问地图。不保证写入发生在读取之前。
    • 哦,让我更清楚一点。 HashMap 是一个单例,所以它不是一个新对象
    • 是的,如果你想要可靠的线程安全,每个方法都必须同步。如果这是一个问题,我肯定会推荐从 ConcurrentHashMap 继承。
    • @Dan Smith 如果您无法更改正在使用的地图,那么您必须在此范例中跟踪它的使用。更难。
    • @DanSmith - 从Singleton 的角度来看,它不会改变任何东西。
    【解决方案4】:

    最好的办法是彻底改变概念。您实际上想要对您的地图执行定期轮询。但是你想如何检测到地图发生了变化?比较什么?有大小吗?但是,如果删除了一个条目并添加了另一个条目,则 map 不会更改大小。

    您可能想比较您在上次迭代期间创建的地图克隆?但首先,地图可能非常大(例如,有 1 亿个条目)。您必须遍历第一个映射以确保所有条目都在第二个映射中,然后遍历第二个映射以检查所有键是否都在第一个映射中。否则你会错过额外的条目。

    我有什么建议>?我建议使用包装器包装任何地图,该包装器调用常规地图的所有方法,除了一个:put()。此方法调用您的真实地图的 put 但也会创建更改特定条目的事件。在这种情况下,您将实时收到更改通知,而不是 10 分钟。

    公共类 NotifyableMap 实现 Map { 私人最终地图地图; public NotifyableMap(Map map) } {this.map = map;}

     public V put(K key, V value) {
         V oldValue = map.put(key, value);
         if (!value.equals(oldValue)) { // TODO: make this check null-safe
             // notify listeners here
         }
     }
    
     // all other methods are trivial 
    

    }

    【讨论】:

      【解决方案5】:

      创建一个类来包装您的数据结构,当修改操作通过您的包装器更新lastModified 字段时。当您的线程唤醒时,只需检查 lastModified 字段,看看您是否需要做一些事情。

      类似这样的:

      import java.util.Date;
      import java.util.HashMap;
      import java.util.Map;
      
      public class ModTimeMap<K, V> extends HashMap<K, V> {
      
          private Date lastModified;
      
          public Date getLastModified() {
              return lastModified;
          }
      
          @Override
          public void clear() {
              updateLastModified();
              super.clear();
          }
      
          @Override
          public V put(K arg0, V arg1) {
              updateLastModified();
              return super.put(arg0, arg1);
          }
      
          @Override
          public void putAll(Map<? extends K, ? extends V> arg0) {
              updateLastModified();
              super.putAll(arg0);
          }
      
          @Override
          public V remove(Object arg0) {
              updateLastModified();
              return super.remove(arg0);
          }
      
          private void updateLastModified() {
              lastModified = new Date();// now
          }
      
      }
      

      我建议公开一些构造函数并添加一个串行版本 ID,但这会给你一个尝试的想法。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多