你必须同步并发集合吗?

Do you have to synchronize concurrent collections?

想象一下下面的例子: 一个应用程序启动两个线程。 Provider class 持有并发集合并向其写入数据。消费者从集合中读取数据。

下面的代码是正确的还是我必须添加同步?

public class Application{
    public static void main(String...args) throws Exception{
        Provider p = new Provider();
        new Thread(p).start();
        new Thread(new Consumer(p)).start();

        // Make sure the example stops after 60 seconds
        Thread.sleep(1000*60);
        System.exit(0);
    }
}

/**The Provider (writes data to concurrent collection)*/
class Provider implements Runnable{

    private ConcurrentMap<Integer, String> map 
        = new ConcurrentHashMap<Integer, String>(20, 0.5f, 1);

    public void run(){
         Integer i = 1;
         while(true){
             try {
                 Thread.sleep(500);
             } catch (InterruptedException ignore) {
             }
             // Synchronization ?
             map.put(i, i.toString());
             i++;
         }
    }

    public ConcurrentMap<Integer, String> getMap(){
         // Synchronization ?
         return map;
    }

}

/**The Consumer (reads data from concurrent collection)*/
class Consumer implements Runnable{

    private Provider provider;

    public Consumer(Provider p){
        provider = p;
    }

    public void run(){
        while(true){
             try {
                 Thread.sleep(1000);
             } catch (InterruptedException ignore) {
             }
            // Synchronization ?
            ConcurrentMap<Integer, String> m = provider.getMap();
            if(m!=null)
                for(String s: m.values())
                    System.out.print(s); 
            System.out.println();               
        }
    }

}

来自ConcurrentHashMap documentation

For aggregate operations such as putAll and clear, concurrent retrievals may reflect insertion or removal of only some entries. Similarly, Iterators, Spliterators and Enumerations return elements reflecting the state of the hash table at some point at or since the creation of the iterator/enumeration. They do not throw ConcurrentModificationException. However, iterators are designed to be used by only one thread at a time. Bear in mind that the results of aggregate status methods including size, isEmpty, and containsValue are typically useful only when a map is not undergoing concurrent updates in other threads. Otherwise the results of these methods reflect transient states that may be adequate for monitoring or estimation purposes, but not for program control.

所以您不需要同步,因为您不会得到ConcurrentModificationException。您是否想要取决于您的程序逻辑。

并发集合的变化是线程安全的,但不保证数据一致性。

备用 Collections.synchronized[...] 习语与显式同步相结合,将保证线程安全 数据一致性(以性能为代价)。