我正在维护一些旧代码,并在synchronized
上使用ConcurrentHashMap
关键字找到了一些实现。对我来说似乎不必要:
public class MyClass{
private final Map<MyObj, Map<String, List<String>>> conMap = new ConcurrentHashMap<>();
//...
//adding new record into conMap:
private void addToMap(MyObj id, String name, String value){
conMap.putIfAbsent(id, new ConcurrentHashMap<>());
Map<String, List<String>> subMap = conMap.get(id);
synchronized(subMap){ // <-- is it necessary?
subMap.putIfAbsent(name, new ArrayList<>());
subMap.get(name).add(value);
}
}
//...
public void doSomthing((MyObj id){
List<Map<String, List<String>>> mapsList = new LinkedList<>();
for(MyObj objId: conMap.keySet()){
if(objId.key1.equals(id.key1)){
mapsList.add(conMap.get(objId));
}
}
for(Map<String, List<String>> map: mapsList){
synchronized(map){ // <-- is it necessary?
if(timeout <= 0){
log(map.size());
for(List<String> value: map.values(){
log(id, value);
}
}
else{
int sum = 0;
for(map.Entry<String, List<String>> val: map.entrySet()){
sum += val.getValue().size();
}
log(sum);
map.wait(timeout);
}
}
//...
}
因此,对已经并发的对象使用synchronized
键是否合理?还是那是两个不同的东西?
答案 0 :(得分:2)
ConcurrentHashMap同步每个单独的方法调用本身,以便其他线程无法访问该映射(并可能破坏该映射的内部数据结构)。
Synchronized块可同步两个或多个连续的方法调用,从而使其他线程无法修改调用之间的数据结构(就应用程序逻辑而言,这可能会破坏数据的一致性)。
请注意,只有在使用相同监视对象从同步块执行对HashMap的所有访问的情况下,synchornized块才起作用。
答案 1 :(得分:2)
这是必要的,因为多个线程可能会尝试同时附加到同一ArrayList
上。 synchonized
显然可以防止ArrayList
不同步。
自Java 8以来,我们有了computeIfAbsent
,这意味着可以简化正在执行的puts及其后面的gets。我会这样写,不需要同步:
conMap.computeIfAbsent(id, k -> new ConcurrentHashMap<>())
.computeIfAbsent(name, k -> new CopyOnWriteArrayList<>()) // or other thread-safe list
.add(value);
答案 2 :(得分:2)
在这种情况下:
synchronized(subMap){ // <-- is it necessary?
subMap.putIfAbsent(name, new ArrayList<>());
subMap.get(name).add(value);
}
synchronized
是必需的。没有它,您可能会有两个线程同时更新相同的ArrayList
实例。由于ArrayList
也不是线程安全的,因此addToMap
方法也不是线程安全的。
在这种情况下:
synchronized(map){ // <-- is it necessary?
if(/*condition*/){
log(map.size());
for(List<String> value: map.values(){
log(id, value);
}
}
else{
int sum = 0;
for(map.Entry<String, List<String>> val: map.entrySet()){
sum += val.getValue().size();
}
log(sum);
map.wait(timeout);
}
synchronized
是必需的。
在if
分支中,log
方法(或从中调用的方法)可能会调用ArrayList::toString
,这将迭代每个ArrayList
。如果没有在子图级别进行同步,则另一个线程可能会同时进行add
(例如addToMap
调用)。这意味着存在内存危险,并且在ConcurrentModificationException
方法中可能toString()
是可能的。
在else
分支中,size()
调用正在访问子图中每个ArrayList
中的size字段。如果不在子图级别进行同步,则这些列表之一可能同时存在add
。这可能导致size()
方法返回陈旧的值。此外,在迭代时,不能保证您会看到添加到子地图的地图条目。如果这些事件中的任何一个发生,则sum
可能不正确。 (是否真的有问题取决于此方法的要求:不正确的计数 是可以接受的。)
答案 3 :(得分:1)
其他答案还不够充分……
Schema::create('users', function (Blueprint $table) {
$table->increments('id');
$table-> string('name');
$table-> string('email')->unique();
$table-> string('password');
$table->rememberToken();
$table->timestamps();
$table -> string('first_name') -> nullable();
$table -> string('middle_name') -> nullable();
$table -> string('last_name') -> nullable();
$table -> string('city') -> nullable();
$table -> integer('role') -> unsigned();
});
有必要吗?很难说。
什么是 for(Map<String, List<String>> map: mapsList){
synchronized(map){ // <-- is it necessary?
if(/*condition*/){
...iterate over map...
}
else {
...iterate over map...
}
}
}
?在线程/*condition*/
上进行同步之后,但在线程B执行两个分支中的任何一个之前或同时,在线程map
上进行同步是否会阻止其他线程A更改/*condition*/
的值?如果是这样,那么synchronized
块可能非常重要。
那些迭代怎么样?在线程map
上进行同步是否可以防止其他一些线程A在迭代线程B时更改映射的内容?如果是这样,那么synchronized
块可能非常重要。