Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.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 迭代ConcurrentHashMap值是否线程安全?_Java_Multithreading_Concurrency_Thread Safety_Concurrenthashmap - Fatal编程技术网

Java 迭代ConcurrentHashMap值是否线程安全?

Java 迭代ConcurrentHashMap值是否线程安全?,java,multithreading,concurrency,thread-safety,concurrenthashmap,Java,Multithreading,Concurrency,Thread Safety,Concurrenthashmap,在javadoc for中,有以下内容: 检索操作(包括get)通常不会阻塞,因此可能与更新操作(包括put和remove)重叠。检索反映了最近完成的更新操作在开始时的结果。对于聚合操作(如putAll和clear),并发检索可能只反映插入或删除某些条目。类似地,迭代器和枚举返回反映哈希表在迭代器/枚举创建时或创建之后某个点的状态的元素。它们不会抛出ConcurrentModificationException但是,迭代器设计为一次只能由一个线程使用。 这是什么意思?如果我尝试同时使用两个线程迭

在javadoc for中,有以下内容:

检索操作(包括get)通常不会阻塞,因此可能与更新操作(包括put和remove)重叠。检索反映了最近完成的更新操作在开始时的结果。对于聚合操作(如putAll和clear),并发检索可能只反映插入或删除某些条目。类似地,迭代器和枚举返回反映哈希表在迭代器/枚举创建时或创建之后某个点的状态的元素。它们不会抛出ConcurrentModificationException但是,迭代器设计为一次只能由一个线程使用。

这是什么意思?如果我尝试同时使用两个线程迭代映射,会发生什么?如果我在迭代映射时从映射中放置或移除一个值,会发生什么情况?

可能会让您有一个很好的了解

ConcurrentHashMap通过稍微放松对调用方的承诺来实现更高的并发性。检索操作将返回最近完成的插入操作插入的值,也可能返回同时进行的插入操作添加的值(但在任何情况下都不会返回无意义的结果)ConcurrentHashMap返回的迭代器。迭代器()将最多返回每个元素一次,并且不会抛出ConcurrentModificationException,但可能会或可能不会反映自构造迭代器以来发生的插入或删除。在迭代集合时,不需要(甚至不可能)表范围的锁定来提供线程安全性。ConcurrentHashMap可以作为synchronizedMap或Hashtable的替代品,用于任何不依赖于锁定整个表以防止更新的应用程序

关于这一点:

但是,迭代器设计为一次只能由一个线程使用


这意味着,在两个线程中使用ConcurrentHashMap生成的迭代器是安全的,但可能会在应用程序中导致意外结果。

这意味着不应在多个线程之间共享迭代器对象。创建多个迭代器并在单独的线程中同时使用它们是可以的

这是什么意思

这意味着从
ConcurrentHashMap
中获得的每个迭代器都被设计为由单个线程使用,不应该被传递。这包括for-each循环提供的语法糖

如果我尝试同时使用两个线程迭代映射,会发生什么

如果每个线程使用自己的迭代器,它将按预期工作

如果在迭代映射时从映射中放置或移除值,会发生什么情况

如果您这样做,可以保证事情不会中断(这是
ConcurrentHashMap
中“并发”的部分含义)。但是,不能保证一个线程将看到另一个线程执行的映射更改(不从映射中获取新的迭代器)。迭代器保证在创建时反映映射的状态。更进一步的更改可能会反映在迭代器中,但不一定要反映在迭代器中

总之,这样的声明

for (Object o : someConcurrentHashMap.entrySet()) {
    // ...
}

几乎每次你看到它都会很好(或者至少是安全的)

您可以使用该类测试两个访问线程和一个对
ConcurrentHashMap的共享实例进行变异的线程:

import java.util.Map;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ConcurrentMapIteration
{
  private final Map<String, String> map = new ConcurrentHashMap<String, String>();

  private final static int MAP_SIZE = 100000;

  public static void main(String[] args)
  {
    new ConcurrentMapIteration().run();
  }

  public ConcurrentMapIteration()
  {
    for (int i = 0; i < MAP_SIZE; i++)
    {
      map.put("key" + i, UUID.randomUUID().toString());
    }
  }

  private final ExecutorService executor = Executors.newCachedThreadPool();

  private final class Accessor implements Runnable
  {
    private final Map<String, String> map;

    public Accessor(Map<String, String> map)
    {
      this.map = map;
    }

    @Override
    public void run()
    {
      for (Map.Entry<String, String> entry : this.map.entrySet())
      {
        System.out.println(
            Thread.currentThread().getName() + " - [" + entry.getKey() + ", " + entry.getValue() + ']'
        );
      }
    }
  }

  private final class Mutator implements Runnable
  {

    private final Map<String, String> map;
    private final Random random = new Random();

    public Mutator(Map<String, String> map)
    {
      this.map = map;
    }

    @Override
    public void run()
    {
      for (int i = 0; i < 100; i++)
      {
        this.map.remove("key" + random.nextInt(MAP_SIZE));
        this.map.put("key" + random.nextInt(MAP_SIZE), UUID.randomUUID().toString());
        System.out.println(Thread.currentThread().getName() + ": " + i);
      }
    }
  }

  private void run()
  {
    Accessor a1 = new Accessor(this.map);
    Accessor a2 = new Accessor(this.map);
    Mutator m = new Mutator(this.map);

    executor.execute(a1);
    executor.execute(m);
    executor.execute(a2);
  }
}
这是什么意思


这意味着您不应该尝试在两个线程中使用相同的迭代器。如果您有两个线程需要迭代键、值或条目,那么每个线程都应该创建并使用自己的迭代器

如果我尝试同时使用两个线程迭代映射,会发生什么

现在还不完全清楚如果你违反了这条规则会发生什么。您可能会遇到混乱的行为,就像(例如)两个线程试图在不同步的情况下读取标准输入一样。您还可以获得非线程安全行为

但是,如果两个线程使用不同的迭代器,您应该不会有问题

如果在迭代映射时从映射中放置或移除值,会发生什么情况


这是一个单独的问题,但您引用的javadoc部分充分回答了这个问题。基本上,迭代器是线程安全的,但是没有定义迭代器返回的对象序列中是否会反映任何并发插入、更新或删除的效果。实际上,这可能取决于更新在映射中的位置。

有没有任何原因不将I-In迭代器大写?“因为它是类的名称,所以可能不那么令人困惑了。”比尔·米歇尔,现在我们进入了发帖礼仪的语义学。我认为他应该把迭代器作为一个迭代器的javadoc链接,或者至少把它放在内联代码注释(`)中。那么,如果在迭代过程中,另一个线程从映射中删除了一个对象o10,会发生什么呢?即使o10被删除了,我还能在迭代中看到它吗@如上所述,如果现有迭代器将反映映射的后续更改,则实际上没有指定。所以我不知道,根据规范,没有人知道(不看代码,这可能会随着运行时的每次更新而改变)。所以你不能依赖它。但是我在迭代一个
ConcurrentHashMap
时仍然得到了一个
ConcurrentModificationException
,为什么?@KimiChiu你可能应该发布一个新的问题,提供触发该异常的代码,但我高度怀疑它是否直接源于并发容器的迭代。除非Java实现有缺陷。您确定“在访问器线程之间共享相同的迭代器会导致死锁”吗?文件说读取没有被阻止,我尝试了你的程序,没有死锁发生。虽然迭代结果将是w
import java.util.Iterator;
import java.util.Map;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ConcurrentMapIteration
{
  private final Map<String, String> map = new ConcurrentHashMap<String, String>();
  private final Iterator<Map.Entry<String, String>> iterator;

  private final static int MAP_SIZE = 100000;

  public static void main(String[] args)
  {
    new ConcurrentMapIteration().run();
  }

  public ConcurrentMapIteration()
  {
    for (int i = 0; i < MAP_SIZE; i++)
    {
      map.put("key" + i, UUID.randomUUID().toString());
    }
    this.iterator = this.map.entrySet().iterator();
  }

  private final ExecutorService executor = Executors.newCachedThreadPool();

  private final class Accessor implements Runnable
  {
    private final Iterator<Map.Entry<String, String>> iterator;

    public Accessor(Iterator<Map.Entry<String, String>> iterator)
    {
      this.iterator = iterator;
    }

    @Override
    public void run()
    {
      while(iterator.hasNext()) {
        Map.Entry<String, String> entry = iterator.next();
        try
        {
          String st = Thread.currentThread().getName() + " - [" + entry.getKey() + ", " + entry.getValue() + ']';
        } catch (Exception e)
        {
          e.printStackTrace();
        }

      }
    }
  }

  private final class Mutator implements Runnable
  {

    private final Map<String, String> map;
    private final Random random = new Random();

    public Mutator(Map<String, String> map)
    {
      this.map = map;
    }

    @Override
    public void run()
    {
      for (int i = 0; i < 100; i++)
      {
        this.map.remove("key" + random.nextInt(MAP_SIZE));
        this.map.put("key" + random.nextInt(MAP_SIZE), UUID.randomUUID().toString());
      }
    }
  }

  private void run()
  {
    Accessor a1 = new Accessor(this.iterator);
    Accessor a2 = new Accessor(this.iterator);
    Mutator m = new Mutator(this.map);

    executor.execute(a1);
    executor.execute(m);
    executor.execute(a2);
  }
}
import java.util.Iterator;
import java.util.Map;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ConcurrentMapIteration
{
  private final Map<String, String> map = new ConcurrentHashMap<String, String>();
  private final Iterator<Map.Entry<String, String>> iterator;

  private final static int MAP_SIZE = 100000;

  public static void main(String[] args)
  {
    new ConcurrentMapIteration().run();
  }

  public ConcurrentMapIteration()
  {
    for (int i = 0; i < MAP_SIZE; i++)
    {
      map.put("key" + i, UUID.randomUUID().toString());
    }
    this.iterator = this.map.entrySet().iterator();
  }

  private final ExecutorService executor = Executors.newCachedThreadPool();

  private final class Accessor implements Runnable
  {
    private final Iterator<Map.Entry<String, String>> iterator;

    public Accessor(Iterator<Map.Entry<String, String>> iterator)
    {
      this.iterator = iterator;
    }

    @Override
    public void run()
    {
      while (iterator.hasNext())
      {
        Map.Entry<String, String> entry = iterator.next();
        try
        {
          String st =
              Thread.currentThread().getName() + " - [" + entry.getKey() + ", " + entry.getValue() + ']';
        } catch (Exception e)
        {
          e.printStackTrace();
        }

      }
    }
  }

  private final class Mutator implements Runnable
  {

    private final Random random = new Random();

    private final Iterator<Map.Entry<String, String>> iterator;

    private final Map<String, String> map;

    public Mutator(Map<String, String> map, Iterator<Map.Entry<String, String>> iterator)
    {
      this.map = map;
      this.iterator = iterator;
    }

    @Override
    public void run()
    {
      while (iterator.hasNext())
      {
        try
        {
          iterator.remove();
          this.map.put("key" + random.nextInt(MAP_SIZE), UUID.randomUUID().toString());
        } catch (Exception ex)
        {
          ex.printStackTrace();
        }
      }

    }
  }

  private void run()
  {
    Accessor a1 = new Accessor(this.iterator);
    Accessor a2 = new Accessor(this.iterator);
    Mutator m = new Mutator(map, this.iterator);

    executor.execute(a1);
    executor.execute(m);
    executor.execute(a2);
  }
}