在用于ConcurrentHashMap的javadoc中是:
检索操作(包括get)一般不会阻塞,因此可能会与更新操作(包括put和remove)重叠。检索反映最近完成的更新操作的结果,这些操作在开始时保持不变。对于putAll和clear等聚合操作,并发检索可能只反映某些条目的插入或删除。类似地,迭代器和枚举返回元素,反映在迭代器/枚举创建时或之后的某个时间点的哈希表的状态。它们不抛出ConcurrentModificationException。但是,迭代器被设计为一次只能由一个线程使用。
什么意思?如果我尝试同时使用两个线程迭代map,会发生什么?如果我在迭代映射时放入或删除一个值,会发生什么?
发布于 2010-09-22 19:17:51
这是什么意思?
这意味着从ConcurrentHashMap
获得的每个迭代器都被设计为供单个线程使用,不应该到处传递。这包括for-each循环提供的语法糖。
如果我试图同时用两个线程迭代映射,会发生什么?
如果每个线程都使用自己的迭代器,那么它将按预期工作。
如果我在迭代映射时放入或删除一个值,会发生什么?
如果这样做,可以保证不会中断(这是ConcurrentHashMap
中“并发”的一部分意思)。但是,不能保证一个线程将看到另一个线程执行的映射的更改(不从映射中获取新的迭代器)。迭代器保证反映映射在创建时的状态。进一步的更改可能会反映在迭代器中,但它们不是必须的。
总之,像这样的语句
for (Object o : someConcurrentHashMap.entrySet()) {
// ...
}
几乎每次你看到它都会很好(或者至少是安全的)。
发布于 2010-09-22 19:18:05
您可以使用这个类来测试两个访问线程和一个改变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);
}
}
不会抛出异常。
在访问器线程之间共享相同的迭代器可能会导致死锁:
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);
}
}
一旦你开始在存取器和赋值器线程之间共享相同的Iterator<Map.Entry<String, String>>
,java.lang.IllegalStateException
就会开始弹出。
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);
}
}
发布于 2010-09-22 19:14:22
这意味着您不应该在多个线程之间共享迭代器对象。创建多个迭代器并在单独的线程中并发使用它们是很好的。
https://stackoverflow.com/questions/3768554
复制相似问题