Java 从缓存获取的集合中的线程安全性
我偶然发现了以下代码:Java 从缓存获取的集合中的线程安全性,java,multithreading,thread-safety,Java,Multithreading,Thread Safety,我偶然发现了以下代码: public static final Map<String, Set<String>> fooCacheMap = new ConcurrentHashMap<>(); publicstaticfinalmap fooCacheMap=newconcurrenthashmap(); 通过rest控制器方法访问此缓存: public void fooMethod(String fooId) { Set<String&g
public static final Map<String, Set<String>> fooCacheMap = new ConcurrentHashMap<>();
publicstaticfinalmap fooCacheMap=newconcurrenthashmap();
通过rest控制器方法访问此缓存:
public void fooMethod(String fooId) {
Set<String> fooSet = cacheMap.computeIfAbsent(fooId, k -> new ConcurrentSet<>());
//operations with fooSet
}
公共void foodmethod(字符串fooId){
Set fooSet=cacheMap.computeIfAbsent(fooId,k->newconcurrentset());
//使用fooSet的操作
}
ConcurrentSet
真的有必要吗?当我确定只能通过此方法访问集合时?当您在控制器中使用它时,多个线程可以同时调用您的方法(例如,多个并行请求可以调用您的方法)
由于此方法看起来没有以任何方式同步,因此这里可能需要ConcurrentSet。当您在控制器中使用它时,多个线程可以同时调用您的方法(例如,多个并行请求可以调用您的方法) 由于此方法看起来没有任何同步方式,因此这里可能需要ConcurrentSet ConcurrentSet真的有必要吗 可能,可能不是。我们不知道这个代码是如何使用的 但是,假设它是以多线程方式使用的(特别是:两个线程可以同时调用
fooMethod
),则是
ConcurrentHashMap
中的原子性仅保证每次调用ComputeFabSent
。一旦完成,锁就被释放,其他线程就可以调用该方法。因此,对返回值的访问不是原子的,因此在访问该值时可以得到线程推断
关于“我需要` ConcurrentSet'吗?”?否:您可以这样做,以便对集合的访问是原子的:
cacheMap.compute(fooId, (k, fooSet) -> {
if (fooSet == null) fooSet = new HashSet<>();
// Operations with fooSet
return v;
});
cacheMap.compute(fooId,(k,fooSet)->{
如果(fooSet==null)fooSet=newhashset();
//使用fooSet的操作
返回v;
});
ConcurrentSet真的有必要吗
可能,可能不是。我们不知道这个代码是如何使用的
但是,假设它是以多线程方式使用的(特别是:两个线程可以同时调用fooMethod
),则是
ConcurrentHashMap
中的原子性仅保证每次调用ComputeFabSent
。一旦完成,锁就被释放,其他线程就可以调用该方法。因此,对返回值的访问不是原子的,因此在访问该值时可以得到线程推断
关于“我需要` ConcurrentSet'吗?”?否:您可以这样做,以便对集合的访问是原子的:
cacheMap.compute(fooId, (k, fooSet) -> {
if (fooSet == null) fooSet = new HashSet<>();
// Operations with fooSet
return v;
});
cacheMap.compute(fooId,(k,fooSet)->{
如果(fooSet==null)fooSet=newhashset();
//使用fooSet的操作
返回v;
});
使用并发映射不能保证线程安全。对映射的添加需要在同步块中执行,以确保两个线程不会尝试向映射添加相同的密钥。因此,实际上并不需要并发映射,特别是因为映射本身是静态的和最终的。此外,如果代码修改了映射内的集合(看起来很可能),那么也需要同步
正确的方法是查看地图是否有钥匙。如果不存在,请输入同步块并再次检查该键。这保证了在每次不输入同步块的情况下密钥不存在
集合修改通常也应该发生在同步块中 使用并发映射不能保证线程安全。对映射的添加需要在同步块中执行,以确保两个线程不会尝试向映射添加相同的密钥。因此,实际上并不需要并发映射,特别是因为映射本身是静态的和最终的。此外,如果代码修改了映射内的集合(看起来很可能),那么也需要同步 正确的方法是查看地图是否有钥匙。如果不存在,请输入同步块并再次检查该键。这保证了在每次不输入同步块的情况下密钥不存在
集合修改通常也应该发生在同步块中 问题不在于是否仅使用此方法访问该集,而在于两个线程是否可以同时访问该集。您可以在多个线程中并行运行此代码,谁知道呢。“当我确定只能使用此方法访问集合时?”但您不知道,因为
fooCacheMap
是public
。问题不在于是否仅使用此方法访问集合,而在于两个线程是否可以同时访问集合。您可以在多个线程中并行运行此代码,谁知道呢。“当我确定只能通过此方法访问集合时?”但您不知道,因为fooCacheMap
是public
。您可以甚至应该在不使用同步块的情况下添加到ConcurrentHashMap。ConcurrentHashMap的实现保证了线程安全。使用synchronized锁定它只会降低性能。“确保两个线程不会尝试向映射添加相同的密钥”是相关的要点。您可以甚至应该在不使用synchronized块的情况下添加ConcurrentHashMap。ConcurrentHashMap的实现保证了线程安全。用synchronized锁定它只会降低性能。“确保两个线程不会尝试向映射添加相同的密钥”是相关的要点。