如何以线程安全的方式填充字符串和另一个映射的映射?

时间:2016-12-05 20:59:37

标签: java multithreading concurrency thread-safety guava

我正在使用下面的课程来衡量我的应用指标,其中包括incrementdecrement指标。

public class AppMetrics {
  private final AtomicLongMap<String> metricCounter = AtomicLongMap.create();

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

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

  private AppMetrics() {}     

  public void increment(String name) {
    metricCounter.getAndIncrement(name);
  }

  public AtomicLongMap<String> getMetricCounter() {
    return metricCounter;
  }
}

我从多线程代码调用increment类的AppMetrics方法,通过传递度量标准名称来增加度量标准。

问题陈述:

现在我希望每个metricCounter都有一个clientId字符串。这意味着我们也可以多次获得相同的clientId,有时它会是新的clientId,所以不知怎的,我需要为metricCounter提取clientId地图并增加该特定地图上的指标(我不知道该怎么做)。

正确的方法是什么,记住它必须是线程安全的,并且必须执行原子操作。我想要制作一张这样的地图:

  private final Map<String, AtomicLongMap<String>> clientIdMetricCounterHolder = Maps.newConcurrentMap();

这是正确的方法吗?如果是,那么我如何通过传递clientId作为关键字来填充此地图,它的值将是每个指标的计数器地图。

我在Java 7上。

2 个答案:

答案 0 :(得分:1)

如果您使用地图,则需要同步创建新的AtomicLongMap实例。我建议改用LoadingCache。您最终可能不会使用任何实际的“缓存”功能,但“加载”功能非常有用,因为它将同步为您创建AtomicLongMap个实例。 e.g:

LoadingCache<String, AtomicLongMap<String>> clientIdMetricCounterCache =
        CacheBuilder.newBuilder().build(new CacheLoader<String, AtomicLongMap<String>>() {
            @Override
            public AtomicLongMap<String> load(String key) throws Exception {
                return AtomicLongMap.create();
            }
        });

现在,您可以安全地为任何客户端启动更新度量标准计数,而无需担心客户端是否为新用户。 e.g。

clientIdMetricCounterCache.get(clientId).incrementAndGet(metricName);

答案 1 :(得分:0)

Map<String, Map<String, T>>只是伪装成Map<Pair<String, String>, T>。创建一个MultiKey类:

class MultiKey {
    public String clientId;
    public String name;

    // be sure to add hashCode and equals
}

然后只使用AtomicLongMap<MultiKey>

编辑:

如果定义了一组指标,那么使用这种数据结构查看一个客户的指标就不会太难:

Set<String> possibleMetrics = // all the possible values for "name"

Map<String, Long> getMetricsForClient(String client) {
    return Maps.asMap(possibleMetrics, m -> metrics.get(new MultiKey(client, m));
}

返回的地图将是一个实时不可修改的视图。如果您使用较旧的Java版本,可能会更冗长,但它仍然可能。