【问题标题】:ConcurrentHashMap vs Synchronized HashMapConcurrentHashMap 与同步 HashMap
【发布时间】:2010-11-20 11:32:51
【问题描述】:

HashMapConcurrentHashMap 上使用包装类SynchronizedMap 有什么区别?

是否只能在迭代 HashMap 时对其进行修改 (ConcurrentHashMap)?

【问题讨论】:

    标签: java core


    【解决方案1】:

    同步HashMap

    1. 每个方法都使用 object level lock 进行同步。所以 synchMap 上的 get 和 put 方法获取了一个锁。

    2. 锁定整个集合是一种性能开销。当一个线程持有锁时,没有其他线程可以使用该集合。

    ConcurrentHashMap 是在 JDK 5 中引入的。

    1. 在对象级别没有锁定,锁定的粒度要细得多。对于ConcurrentHashMap,锁可能位于hashmap bucket level

    2. 较低级别锁定的效果是您可以拥有并发的读取器和写入器,这对于同步集合是不可能的。这会带来更大的可扩展性。

    3. ConcurrentHashMap 不会抛出 ConcurrentModificationException,如果一个线程尝试修改它,而另一个线程正在对其进行迭代。

    这篇文章Java 7: HashMap vs ConcurrentHashMap 非常好读。强烈推荐。

    【讨论】:

    • 那么HashtableSynchronized HashMap有什么区别呢?
    • 在 ConcurrentHashMap 和 Synchronized HashMap 之间,你推荐哪一个?
    • 值得一提的是ConcurrentHashMapsize() 结果可能已经过时了。根据“Java Concurrency in Practice”一书,size() 允许返回近似值而不是精确计数。所以这个方法要慎用。
    • @roottraveller 用于 Hashtable 和同步 HashMap stackoverflow.com/questions/8875680/…
    【解决方案2】:

    同步 HashMap

    1. 锁定机制 - 它锁定整个地图,因此多个线程不能同时访问地图。因此,性能相对较差。

    2.Null 键或值 - 它将允许 null 作为键或值。

    3.并发修改异常 - 同步map返回的迭代器抛出并发修改异常

    ConcurrentHashMap

    1.锁定机制 - 锁定部分,Concurrent hashmap允许并发读写。所以性能相对比同步地图要好

    2.Null 键或值 - 它不允许 null 作为键或值。如果使用它将在运行时抛出 java.lang.NullPointerException

    3.并发修改异常 - 不抛出并发修改异常。

    import java.util.Map;
    import java.util.concurrent.ConcurrentHashMap;
    
    public class Ex_ConcurrentHashMap {
    
        public static void main(String[] args) {
            
            Map<String, String> map = new ConcurrentHashMap<>();
            map.put("one", "one");
            map.put("two", "two");
            map.put("three", "three");
            System.out.println("1st  map : "+map);
            String key = null;
            for(Map.Entry<String, String> itr : map.entrySet())
            {
                key = itr.getKey();
                if("three".equals(key))
                {
                    map.put("FOUR", "FOUR");
                }
                System.out.println(key+" ::: "+itr.getValue());
            }
            System.out.println("2nd  map : "+map);
            //map.put("FIVE", null);//java.lang.NullPointerException
            map.put(null, "FIVE");//java.lang.NullPointerException
            System.out.println("3rd  map : "+map);
        }
    }
    

    同步 HashMap 示例

    import java.util.Collections;
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.Map;
    import java.util.Map.Entry;
    
    public class Ex_Synchronizedmap {
    
        public static void main(String[] args) {
            
            Map<String, String> map = new HashMap<>();
            
            map.put("one", "one");
            map.put("two", "two");
            map.put("three", "three");
            map.put("FOUR", null);
            map.put(null, "FIVE");
            
            System.out.println("map : "+map);
            
            Map<String, String> map1 = 
                    Collections.synchronizedMap(map);
            System.out.println("map1 : "+map1);
            
            String key = null;
            for(Map.Entry<String, String> itr : map1.entrySet())
            {
                key = itr.getKey();
                if("three".equals(key))
                {
                    map1.put("ABC", "ABC");
                }
                System.out.println(key+" ::: "+itr.getValue());
            }
            
            System.out.println("New Map :: "+map1);
            
            
            
            Iterator<Entry<String, String>> iterator = map1.entrySet().iterator();
            int i = 0;
            while(iterator.hasNext())
            {
                if(i == 1)
                {
                    map1.put("XYZ", "XYZ");
                }
                Entry<String, String> next = iterator.next();
                System.out.println(next.getKey()+" :: "+next.getValue());
                i++;
            }
        }
    
    }
    

    【讨论】:

      【解决方案3】:

      SynchronizedMapConcurrentHashMap 都是线程安全类,可以在多线程应用程序中使用,它们之间的主要区别在于它们如何实现线程安全。

      【讨论】:

        【解决方案4】:

        我们可以同时使用 ConcurrentHashMap 和 synchronisedHashmap 来实现线程安全。但是如果你看看他们的架构,就会有很大的不同。

        1. 同步哈希图

        它将保持对象级别的锁定。因此,如果您想执行 put/get 之类的任何操作,则必须先获取锁。同时,不允许其他线程执行任何操作。因此,一次只能有一个线程对此进行操作。所以这里的等待时间会增加。与 ConcurrentHashMap 相比,我们可以说性能相对较低。

        1. ConcurrentHashMap

        它将保持段级别的锁定。它有 16 个段,默认保持并发级别为 16。所以一次可以有 16 个线程对 ConcurrentHashMap 进行操作。此外,读操作不需要锁。所以任意数量的线程都可以对其执行get操作。

        如果thread1想在segment 2上执行put操作,thread2想在segment 4上执行put操作,那么这里是允许的。也就是说,16个线程一次可以对ConcurrentHashMap进行update(put/delete)操作。

        这样在这里的等待时间会更少。因此性能相对优于 synchronisedHashmap。

        【讨论】:

        • 非常好的解释非常感谢
        【解决方案5】:

        A simple performance test for ConcurrentHashMap vs Synchronized HashMap 。测试流程是在一个线程中调用put,同时在Map 的三个线程中调用get。正如@trshiv 所说,ConcurrentHashMap 对于没有锁的读取操作具有更高的吞吐量和速度。结果是当操作次数超过10^7,ConcurrentHashMap 比同步HashMap 快2x

        【讨论】:

          【解决方案6】:

          根据 java 文档

          Hashtable 和 Collections.synchronizedMap(new HashMap()) 是 同步。但是 ConcurrentHashMap 是“并发的”。

          并发集合是线程安全的,但不受单个排除锁的控制。

          在 ConcurrentHashMap 的特殊情况下,它安全地允许 任意数量的并发读取以及可调数量的 并发写入。 “同步”类在您需要时很有用 为了防止所有通过单个锁访问集合,在 以较差的可扩展性为代价。

          在其他情况下,多个 线程应该访问一个公共集合,“并发” 版本通常是可取的。不同步的集合是 当任一集合不共享或可访问时更可取 仅当持有其他锁时。

          【讨论】:

            【解决方案7】:

            ConcurrentHashMap 允许并发访问数据。整个地图被分成几个部分。

            读操作,即。 get(Object key) 即使在段级别也不同步。

            但是写操作,即。 remove(Object key), get(Object key) 在段级别获取锁。整个地图只有一部分被锁定,其他线程仍然可以从除了锁定的一个段之外的各个段中读取值。

            SynchronizedMap 另一方面,在对象级别获取锁。所有线程都应该等待当前线程而不管操作(读/写)。

            【讨论】:

              【解决方案8】:

              ConcurrentHashMap:

              1) 两个映射都是 Map 接口的线程安全实现。

              2) ConcurrentHashMap 用于在预期高并发的情况下实现更高的吞吐量。

              3) 对象级别没有锁定。

              同步哈希图:

              1) 每个方法都使用对象级锁进行同步。

              【讨论】:

                【解决方案9】:

                SynchronizedMap 上的方法持有对象上的锁,而在ConcurrentHashMap 中有一个“锁条带化”的概念,其中锁被持有在内容的桶上。从而提高了可扩展性和性能。

                【讨论】:

                  【解决方案10】:

                  简短的回答:

                  两个映射都是Map 接口的线程安全实现。 ConcurrentHashMap 用于在预期高并发的情况下实现更高的吞吐量。

                  Brian Goetz 关于ConcurrentHashMap 背后想法的article 是一本非常好的读物。强烈推荐。

                  【讨论】:

                  • 那是什么? HashMap:注意这个实现是不同步的,防止意外不同步访问map:Map m = Collections.synchronizedMap(new HashMap(...));docs.oracle.com/javase/7/docs/api/java/util/HashMap.html
                  • “Brian Goetz 的文章……非常好读。” - 他的《Java Concurrency in Practice》一书更是如此。
                  【解决方案11】:

                  两者都是 HashMap 的同步版本,其核心功能和内部结构有所不同。

                  ConcurrentHashMap 由内部段组成,在概念上可以看作是独立的 HashMap。 所有这些段都可以在高并发执行中被单独的线程锁定。 因此,多个线程可以从 ConcurrentHashMap 获取/放置键值对,而不会相互阻塞/等待。 这是为了提高吞吐量而实现的。

                  Collections.synchronizedMap(),我们得到了一个同步版本的HashMap,它以阻塞的方式被访问。这意味着如果多个线程尝试同时访问 synchronizedMap,它们将被允许以同步的方式一次获取/放置一个键值对。

                  【讨论】:

                    【解决方案12】:

                    ConcurrentHashMap 使用称为lock stripping 的更细粒度的锁定机制来允许更大程度的共享访问。因此,它提供了更好的并发性可扩展性

                    还为 ConcurrentHashMap 返回的迭代器是弱一致,而不是 Synchronized HashMap 使用的快速失败技术

                    【讨论】:

                      【解决方案13】:

                      ConcurrentHashMap 是线程安全的,无需同步整个地图。使用锁完成写入时,读取可能会非常快。

                      【讨论】:

                        猜你喜欢
                        • 1970-01-01
                        • 2010-11-25
                        • 2014-04-24
                        • 1970-01-01
                        • 2012-05-08
                        • 1970-01-01
                        • 1970-01-01
                        • 2014-01-03
                        相关资源
                        最近更新 更多