Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/376.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
具有列表映射的Java并发性_Java_Concurrency - Fatal编程技术网

具有列表映射的Java并发性

具有列表映射的Java并发性,java,concurrency,Java,Concurrency,我有一个java类,一次可以被很多线程访问,我想确保它是线程安全的。该类有一个私有字段,它是字符串到字符串列表的映射。我已将映射实现为ConcurrentHashMap,以确保get和put是线程安全的: public class ListStore { private Map<String, List<String>> innerListStore; public ListStore() { innerListStore = new Concurre

我有一个java类,一次可以被很多线程访问,我想确保它是线程安全的。该类有一个私有字段,它是字符串到字符串列表的映射。我已将映射实现为ConcurrentHashMap,以确保get和put是线程安全的:

public class ListStore {

  private Map<String, List<String>> innerListStore;

  public ListStore() {
    innerListStore = new ConcurrentHashMap<String, List<String>>();
  }
  ...
}
公共类列表存储{
私有地图目录库;
公共列表库(){
innerListStore=新的ConcurrentHashMap();
}
...
}
因此,考虑到映射的get和put是线程安全的,我关心的是映射中存储的列表。例如,考虑下面的方法来检查给定的条目是否存在于商店中的给定列表中(我省略了错误检查的简洁性):

public boolean listEntryExists(字符串listName,字符串listEntry){
List listToSearch=innerListStore.get(listName);
for(字符串入口名:listToSearch){
if(entryName.equals(listEntry)){
返回true;
}
}
返回false;
}
似乎我需要同步此方法的全部内容,因为如果另一个方法在innerListStore.get(listName)中更改了列表的内容,而此方法正在对其进行迭代,则会引发ConcurrentModificationException

这是正确的吗?如果是,我是在innerListStore上同步还是在本地listToSearch变量上同步

更新:感谢您的回复。听起来我可以在列表上同步。有关详细信息,这里是add()方法,它可以在另一个线程中运行listEntryExists()方法的同时运行:

public void add(String listName, String entryName) {

  List<String> addTo = innerListStore.get(listName);
  if (addTo == null) {
    addTo = Collections.synchronizedList(new ArrayList<String>());
    List<String> added = innerListStore.putIfAbsent(listName, addTo);
    if (added != null) {
      addTo = added;
    }
  }

  addTo.add(entryName);
}
public void add(String listName,String entryName){
List addTo=innerListStore.get(listName);
if(addTo==null){
addTo=Collections.synchronizedList(新的ArrayList());
添加的列表=innerListStore.putIfAbsent(listName,addTo);
如果(已添加!=null){
addTo=已添加;
}
}
addTo.add(entryName);
}

如果这是修改存储在映射中的基础列表的唯一方法,并且没有公共方法返回对映射的引用或映射中的条目,那么我可以同步列表本身的迭代吗?这个add()实现是否足够?

如果映射已经是线程安全的,然后,我认为将列表同步到搜索应该会起作用。我不是100%,但我认为它应该有效

synchronized(listToSearch)
{

}

您可以在listToSearch上进行同步(“已同步(listToSearch){…}”)。确保创建列表时没有竞争条件(使用innerListStore.putIfAbsent创建它们)。

如果存储在映射中的列表属于不抛出CME的类型(例如),则可以随意迭代


这可能会引入一些比赛,但是如果你不小心,你可以在just listToSearch上同步,没有理由在任何人只使用一个条目时锁定整个地图

不过,请记住,您需要在列表中修改的任何位置进行同步!同步迭代器不会自动阻止其他人执行add()或其他操作,如果您向他们传递对未同步列表的引用

最安全的方法是将同步列表存储在映射中,然后在迭代时锁定它们,并在返回对列表的引用时记录这些引用,如果用户进行迭代,则必须对其进行同步。在现代JVM中,当没有实际争用发生时,同步是非常便宜的。当然,如果您从未让对其中一个列表的引用逃逸出您的类,那么您可以在内部使用更精细的梳来处理它

或者,您可以使用线程安全列表,例如使用快照迭代器的CopyOnWriteArrayList。您需要什么样的时间点一致性是我们无法为您做出的设计决策。报告还包括对性能特征的有益讨论

似乎我需要同步此方法的全部内容,因为如果另一个方法在innerListStore.get(listName)中更改了列表的内容,而此方法正在对其进行迭代,则会引发ConcurrentModificationException

其他线程是访问列表本身,还是仅通过
ListStore
公开的操作

其他线程调用的操作是否会导致存储在映射中的列表的内容发生更改?还是只在地图上添加/删除条目


如果不同的线程可能导致对相同列表实例的更改,则只需要同步对存储在映射中的列表的访问。如果只允许线程从映射中添加/删除列表实例(即更改映射的结构),则不需要同步。

您可以从


请注意,这将在整个映射上同步,因此可能对您没有多大用处。

由于除了
boolean listEntryExists(String listName,String listEntry)
方法之外,您还没有为列表映射提供任何客户端,我想知道您为什么要存储列表?该结构似乎更自然地是一个
映射
,而
listEntryExists
应该使用
包含
方法(也可在
列表
上找到,但与列表大小成O(n)比):

现在,contains调用可以封装您希望它封装的任何内部并发协议

对于
add
,您可以使用同步包装器(简单,但速度可能较慢),或者如果写入与读取相比不频繁,则使用
ConcurrentMap.replace
实现您自己的写时拷贝策略。例如,使用番石榴
ImmutableSet

public boolean add(String name, String entry) {
  while(true) {
    SetString> set = map.get(name);
    if (set == null) {
      if (map.putIfAbsent(name, ImmutableSet.of(entry))
        return true
      continue;
    }
    if (set.contains(entry)
      return false; // no need to change, already exists
    Set<String> newSet = ImmutableSet.copyOf(Iterables.concat(set, ImmutableSet.of(entry))        
    if (map.replace(name, set, newSet)
      return true;
  }
}
public boolean add(字符串名称、字符串条目){
while(true){
SetString>set=map.get(名称);
if(set==null){
if(map.putIfAbsent(名称,ImmutableSet.of(条目))
返回真值
继续;
}
if(set.contains)(条目)
return false;//不需要更改,已经存在
Set newSet=不可变
public boolean listEntryExists(String name, String entry) {
  SetString> set = map.get(name);
  return (set == null) ? false : set.contains(entry;
}
public boolean add(String name, String entry) {
  while(true) {
    SetString> set = map.get(name);
    if (set == null) {
      if (map.putIfAbsent(name, ImmutableSet.of(entry))
        return true
      continue;
    }
    if (set.contains(entry)
      return false; // no need to change, already exists
    Set<String> newSet = ImmutableSet.copyOf(Iterables.concat(set, ImmutableSet.of(entry))        
    if (map.replace(name, set, newSet)
      return true;
  }
}