ConcurrentHashMap遍历详解

来源:互联网 发布:linux 复制文件覆盖 编辑:程序博客网 时间:2024/05/17 23:17

近期校招面试,问到ConcurrentHashMap遍历问题,今天总结下:

在ConcurrentHashMap中,使用了分段锁机制,所以任意数量的读线程可以并发访问Map、读操作和写操作的线程可以并发访问Map、并且一定数量(默认是使用16个锁)的写线程也可以并发修改Map。ConcurrentHashMap提供的迭代器不会抛出ConcurrentModificationExeption,而且具有弱一致性,它可以容忍并发的修改。当创建迭代器时会遍历已有的元素,并可以(但是不保证)在迭代器被构造后将修改操作反映给容器。
那么,当多个线程同时迭代一个ConcurrentHashMap的时候会发生什么呢?在遍历的时候更新又会发生什么呢?
1,多个线程可以同时迭代ConcurrentHashMap;
2,ConcurrentHashMap保证遍历的时候更新元素不会break(这正是并发容器的意义所在),但是不能保证数据的一致性,而迭代器保证的是:它反映的是创建迭代器时容器的状态。

注意:
从ConcurrentHashMap那里得到的iterator是为单线程设计的,即不可以传递它们,每一个线程都必须有自己的iterator。

例子:

(1)两个读线程和一个修改线程,每个线程各自拥有一个Iterator,并发访问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);  }}

结果:正常运行

(2)两个读线程共享一个Iterator

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);  }}

结果:发生死锁

(3)读线程和修改线程共享一个Iterator

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);  }}

结果:抛出java.lang.IllegalStateException异常。

原创粉丝点击