如何在第一次调用 Singleton 时只更新一次地图?

How to update map only once during the first call to Singleton?

我必须在服务器上进行 HTTP 调用后解析响应。如果响应不成功,则尝试另一个服务器,否则解析成功的响应并填充两个 ConcurrentHashMap 并跳出 for 循环。所有的服务器都会以相同的格式给出相同的准确响应。

下面是我的单例class,它在ProcConfig的构造函数中第一次调用时,调用loadConfig()方法初始化所有东西,然后检查addressToIdMapping映射是否有是否有一个条目。如果它不存在则抛出异常。之后它启动一个后台线程,每 30 分钟调用 loadConfig() 方法来更新 addressToIdMappingprocessToTcpMapping map.

public class ProcConfig {
  private static final Splitter SPLITTER = Splitter.on(',').trimResults().omitEmptyStrings();
  private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
  private final Map<String, Short> addressToIdMapping = new ConcurrentHashMap<>();
  private final Map<DatacenterEnum, List<String>> processToTcpMapping = new ConcurrentHashMap<>();

  private static class Holder {
    private static final ProcConfig INSTANCE = new ProcConfig();
  }

  public static ProcConfig getInstance() {
    return Holder.INSTANCE;
  }

  private ProcConfig() {
    loadConfig();
    checkArgument(!MapUtils.isEmpty(addressToIdMapping), "cannot find id, found '%s'.", addressToIdMapping);
    scheduler.scheduleAtFixedRate(new Runnable() {
      public void run() {
        try {
          loadConfig();
        } catch (Exception ex) {
          // log error
        }
      }
    }, 60, 30, TimeUnit.MINUTES);
  }

  private void loadConfig() {
    // current ipAddress where the program is running
    Optional<String> ipAddress = Utils.getIPAddress();
    List<String> servers = getServers();
    for (String server : servers) {
      try {
        String response = HttpClient.getInstance().execute(makeUrl(server));
        if (Strings.isNullOrEmpty(response) || response.equalsIgnoreCase("KEEP OUT")
            || response.equalsIgnoreCase("NOTHING FOUND")) {
          continue;
        }
        parseConfig(response, ipAddress.get());
        break;
      } catch (Exception ex) {
        // log error
      }
    }
  }

  private void parseConfig(final String response, final String ipAddress) throws IOException {
    List<String> lines = IOUtils.readLines(new StringReader(response));
    for (String line : lines) {
      if (line.contains(ipAddress)) {
        List<String> config = SPLITTER.splitToList(line);
        Short id = Short.parseShort(config.get(2));
        // this map will only have one entry for the ip address where it is running
        addressToIdMapping.put(ipAddress, id);
      } else if (line.contains("process_")) {
        List<String> config = SPLITTER.splitToList(line);
        String procAddr = config.get(0);
        int datacenter = Integer.parseInt(config.get(1));
        int portNumber = Integer.parseInt(config.get(3));
        int numberOfPorts = Integer.parseInt(config.get(4));
        DatacenterEnum colo = Utils.isProd() ? DatacenterEnum.name((byte) datacenter) : DatacenterEnum.DEV;
        List<String> address = makeTcpAddress(procAddr, colo, portNumber, numberOfPorts);
        processToTcpMapping.put(colo, address);
      }
    }
  }

  public Optional<Short> getId() {
    Optional<String> ipAddress = Utils.getIPAddress();
    return Optional.fromNullable(addressToIdMapping.get(ipAddress.get()));
  }
}

现在我的问题是:我只想在第一次调用单例时更新我的​​ addressToIdMapping 映射一次,这样 getId() 方法总是 return 第一次调用时有什么在地图上更新。但是现在它会 return 每更新 30 分钟后地图中的任何内容。例如:当第一次调用这个 class 时,它会更新地图,所以我想在 addressToIdMapping 地图中永远保持相同的值,直到程序为 运行。这可能吗?同样如您所见,我在我的构造函数中做了很多事情。有没有比我现在做的更好的方法来做同样的事情?

一般来说,addressToIdMapping 地图总是只有一个 IP 地址条目,代码在 运行 中。如果 processToTcpMapping 地图每 30 分钟更新一次,我就很好.

您可以在 ConcurrentHashMap 上使用 putIfAbsent 方法,请参阅 documentation

如果条目不存在,它只会更新条目,这意味着一个 IP 地址的条目只会更新一次。