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计数器转换为原子整数,那个么使用外部锁仍然有意义吗?我想是的,因为正如你提到的,我们需要保持整体功能的一致性。是的。即使将计数器设置为原子计数器,为了保持功能一致性,也需要锁。