线程安全而不是在可变状态下同步

时间:2015-12-26 20:52:34

标签: java multithreading

我有这个类有3个读取方法和1个写入方法:

class ResourceClass {

  private static Map resourceMap = new HashMap();

  // New method to update resource
  public void write(String key, Object resource) {
    resourceMap.put(key, resource);
  }

  public Object read(String var1) {
    return resourceMap.get(var1);
  }

  public Object read(String var1, String var2) {
    // .. Do task with var1 and var2
    return resourceMap.get(var1);
  }

  public Object read(String var1, String var2, String var3) {
    // .. Do task with var1, var2 and var3
    return resourceMap.get(var1);
  }  
}

目前,此类仅包含一个写入和3个读取方法来使用静态资源。此配置的问题在于,更新resourceMap的唯一方法是重新启动应用程序,以便再次创建ResourceClassresourceMap将添加到类中供其使用。

我想要的是添加一些动态方式来更新resourceMap而不重新启动服务,但为此我必须使此类线程安全,以便处理write方法来更新resourceMap安全。为此,我可以选择在synchronizedread方法中使用write关键字,因此只有一个线程可以访问resourceMap方法可以解决问题,但也包括其他问题。这些read方法是高并发方法,因此添加synchronized关键字会严重影响服务性能,当然我们也不希望这样做。

是否有任何机构知道一种方法来保持线程读取(不会相互阻塞)但是当有一个线程到write所有read方法时等待写完成并在{ {1}}完成?

2 个答案:

答案 0 :(得分:3)

正如@Mike Mnomonic在评论中所说,ConcurrentHashMap是一个具有可调整并发级别的线程安全映射。与其他哈希映射一样,ConcurrentHashMap具有后备数组;根据您指定的并发级别(默认值为16),每个子阵列只有一个锁定,这个后备阵列被拆分为多个子阵列,例如,如果您的容量为128,并且使用的默认并发级别为16,那么子阵列[0,8]有自己的锁,[8,16]有自己的锁,[16,24]有自己的锁,依此类推,所以两个线程可以写入两个不同的子阵列而不会相互阻塞。

如果写入次数非常少,那么ImmutableMap包含的AtomicReference可能会提高效果。

private final AtomicReference<ImmutableMap<String, Object>> resourceMap;

public void write(String key, Object value) {
    boolean success = false;
    while(!success) {
        ImmutableMap oldMap = resourceMap;
        ImmutableMap.Builder<String, Object> builder = ImmutableMap.builder();
        builder.putAll(resourceMap.entrySet());
        builder.put(key, value);
        success = resourceMap.compareAndSet(oldMap, builder.build());
    }
}

public Object read(String var1) {
    ...
    return resourceMap.get().get(var1); // get map, then get value
}
public Object read(String var1, String var2);
public Object read(String var1, String var2, String var3);

write上复制旧地图,添加新值,并为旧地图交换新地图;如果两个更新试图互相攻击,那么只有一个会成功,同时另一个将继续重试,直到其更新为止。读者不需要关心任何这些 - 他们只是get当前的地图。

答案 1 :(得分:1)

对于高读取重负载,请考虑克隆集合并添加到克隆中,然后将结果分配给字段。

如果可以同时调用write方法,则可能需要进行工作以确保对此方法的访问是“线程安全的”。