Java 处理并发哈希映射时锁的必要性

Java 处理并发哈希映射时锁的必要性,java,concurrency,locking,Java,Concurrency,Locking,下面是我的一个类中的代码: class SomeClass { private Map<Integer, Integer> map = new ConcurrentHashMap<>(); private volatile int counter = 0; final AtomicInteger sum = new AtomicInteger(0); // will be used in other classes/threads too pr

下面是我的一个类中的代码:

 class SomeClass {

   private Map<Integer, Integer> map = new ConcurrentHashMap<>();
   private volatile int counter = 0;
   final AtomicInteger sum = new AtomicInteger(0); // will be used in other classes/threads too
   private ReentrantLock l = new ReentrantLock();

   public void put(String some) {
    l.lock();
    try {
        int tmp = Integer.parseInt(some);
        map.put(counter++, tmp);
        sum.getAndAdd(tmp);
    } finally {
        l.unlock();
    }
   }

   public Double get() {
    l.lock();
    try {
        //... perform some map resizing operation ...
        // some calculations including sum field ...
    } finally {
        l.unlock();
    }
   }

}
class-SomeClass{
私有映射映射=新的ConcurrentHashMap();
专用易失性int计数器=0;
final-AtomicInteger sum=new-AtomicInteger(0);//也将在其他类/线程中使用
private ReentrantLock l=new ReentrantLock();
公共作废put(字符串部分){
l、 锁();
试一试{
int tmp=Integer.parseInt(一些);
map.put(计数器++,tmp);
总收入(tmp);
}最后{
l、 解锁();
}
}
公共双get(){
l、 锁();
试一试{
//…执行一些贴图大小调整操作。。。
//一些计算包括和域。。。
}最后{
l、 解锁();
}
}
}
您可以假设该类将在并发环境中使用


问题是:你认为锁的必要性是什么?这段代码闻起来怎么样?:)

因为当您将计数器作为键放入此地图时,它总是递增
计数器

map.put(counter++, tmp);
当你再次阅读时:

return sum / map.get(counter);
map.get(counter)
将为
null
,因此这将导致NPE(除非您将超过2^32个内容放入map,ofc)。(我假设您的意思是
sum.get()
,否则它将无法编译)

因此,您可以在不使用任何锁的情况下使用等效功能:

class SomeClass {
  public void put(String some) { /* do nothing */ }
  public Double get() {
    throw new NullPointerException();
  }
}

你还没有真正解决你电脑的问题<代码>除数仍将为空,因此不带锁的等效功能为:

class SomeClass {
  private final AtomicInteger sum = new AtomicInteger(0);

  public void put(String some) {
    sum.getAndAdd(Integer.parseInt(some));
  }

  public Double get() {
    return sum.get();
  }
}

让我们看看
public void put(String-some)
中的操作

  • map.put(计数器++,tmp)
  • sum.getandad(tmp)
  • 现在让我们看一下各个部分

  • 计数器
    是一个易失变量。因此,它只提供内存可见性,而不提供原子性。由于
    计数器+++
    是一个复合操作,因此需要一个锁来实现原子性

  • map.put(key,value)
    是原子的,因为它是一个
    ConcurrentHashMap

  • sum.getAndAdd(tmp)
    是原子的,因为它是一个
    AtomicInteger
  • 如您所见,除了
    计数器+++
    之外,其他所有操作都是原子操作。但是,您正试图通过组合所有这些操作来实现某些功能。要在功能级别实现原子性,需要一个锁。这将帮助您避免线程在各个原子操作之间交错时产生意外的副作用


    因此,您需要一个锁,因为
    计数器+++
    不是原子的,您希望结合一些原子操作来实现一些功能(假设您希望它是原子的)。

    因为您要更新put中的多个内容(映射、计数器、总和),所以您需要确保独占访问,以确保它们得到一致的更新。另外,
    计数器+++
    不是原子的。@AndyTurner是的,但我正在用已经获取的监视器执行增量,对吗?是的。我是说这是必要的。关于你的最后一个问题,闻起来很难闻
    new ConcurrentHashMap()
    正在使用原始类型,
    new AtomicInteger(0L)
    甚至不编译,注释“将用于其他类”与
    private
    修饰符相矛盾,一般来说,这段代码的目的不清楚,但看起来好像没有做任何你想做的事情。正如Andy Turner所说,查询单调递增的计数器作为映射键将永远找不到映射。非常感谢!若我将volatile计数器转换为原子整数,那个么使用外部锁仍然有意义吗?我想是的,因为正如你提到的,我们需要保持整体功能的一致性。是的。即使将
    计数器设置为原子计数器,为了保持功能一致性,也需要锁。