Collections.synchronizedMap()、ConcurrentHashMap、Hashtable之间的区别

为什么要比较Hashtable、SynchronizedMap()、ConcurrentHashMap之间的关系?因为常用的HashMap是非线程安全的,不能满足在多线程高并发场景下的需求。

那么为什么说HashTable是线程不安全的?

如何线程安全的使用HashMap

了解了 HashMap 为什么线程不安全,那现在看看如何线程安全的使用 HashMap。这个无非就是以下三种方式:

  • Hashtable
  • ConcurrentHashMap
  • Synchronized Map

Hashtable

那先说说Hashtable,Hashtable源码中是使用 synchronized 来保证线程安全的,比如下面的 get 方法和 put 方法:

public synchronized V get(Object key) {  
       // 省略实现  
}  
public synchronized V put(K key, V value) {  
    // 省略实现  
}

所以当一个线程访问 HashTable 的同步方法时,其他线程如果也要访问同步方法,会被阻塞住。举个例子,当一个线程使用 put 方法时,另一个线程不但不可以使用 put 方法,连 get 方法都不可以,好霸道啊!!!so~~,效率很低,现在基本不会选择它了。

Collections.synchronizedMap()

看了一下源码,synchronizedMap()的实现还是很简单的。

public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) {  

    return new SynchronizedMap<>(m);  

}  /** 

 * @serial include 

 */  private static class SynchronizedMap<K,V>  

    implements Map<K,V>, Serializable {  

    private static final long serialVersionUID = 1978198479659022715L;  

    private final Map<K,V> m;     // Backing Map  

    final Object      mutex;        // Object on which to synchronize  

    SynchronizedMap(Map<K,V> m) {  
        this.m = Objects.requireNonNull(m);  
        mutex = this;  
    }  

    SynchronizedMap(Map<K,V> m, Object mutex) {  
        this.m = m;  
        this.mutex = mutex;  
    }  


    public int size() {  
        synchronized (mutex) {return m.size();}  
    }  

    public boolean isEmpty() {  
        synchronized (mutex) {return m.isEmpty();}  
    }  

    public boolean containsKey(Object key) {  
        synchronized (mutex) {return m.containsKey(key);}  
    }  

    public boolean containsValue(Object value) {  
        synchronized (mutex) {return m.containsValue(value);}  
    }  

    public V get(Object key) {  
        synchronized (mutex) {return m.get(key);}  
    }  



    public V put(K key, V value) {  
        synchronized (mutex) {return m.put(key, value);}  
    } 

    public V remove(Object key) {  
        synchronized (mutex) {return m.remove(key);}  
    }  

    public void putAll(Map<? extends K, ? extends V> map) {  
        synchronized (mutex) {m.putAll(map);}  
    }  

    public void clear() {  
        synchronized (mutex) {m.clear();}  
    }

从源码中可以看出调用 synchronizedMap() 方法后会返回一个 SynchronizedMap 类的对象,而在 SynchronizedMap 类中使用了 synchronized 同步关键字来保证对 Map 的操作是线程安全的。

ConcurrentHashMap

Spring的源码中有很多使用ConcurrentHashMap的地方。需要注意的是,上面博客是基于 Java 7 的,和8有区别,在8中 CHM 摒弃了 Segment(锁段)的概念,而是启用了一种全新的方式实现,利用CAS算法。

下面通过一个具体例子看看Collections.synchronizedMap()和ConcurrentHashMap哪个性能更高。

public class Test {  

      public final static int THREAD_POOL_SIZE = 5;  

      public static Map<String, Integer> crunchifyHashTableObject = null;  

      public static Map<String, Integer> crunchifySynchronizedMapObject = null;  

      public static Map<String, Integer> crunchifyConcurrentHashMapObject = null;  

      public static void main(String[] args) throws InterruptedException {  
         // Test with Hashtable Object  
         crunchifyHashTableObject = new Hashtable<>();  
         crunchifyPerformTest(crunchifyHashTableObject);  
         // Test with synchronizedMap Object  
        crunchifySynchronizedMapObject = Collections.synchronizedMap(new HashMap<String, Integer>());  
         crunchifyPerformTest(crunchifySynchronizedMapObject);  
         // Test with ConcurrentHashMap Object  
         crunchifyConcurrentHashMapObject = new ConcurrentHashMap<>();  
         crunchifyPerformTest(crunchifyConcurrentHashMapObject);  
     }  

     public static void crunchifyPerformTest(final Map<String, Integer> crunchifyThreads) throws InterruptedException {  
         System.out.println("Test started for: " + crunchifyThreads.getClass());  
         long averageTime = 0;  
         for (int i = 0; i < 5; i++) {  
             long startTime = System.nanoTime();  
             ExecutorService crunchifyExServer = Executors.newFixedThreadPool(THREAD_POOL_SIZE);  
             for (int j = 0; j < THREAD_POOL_SIZE; j++) {  
                 crunchifyExServer.execute(new Runnable() {  
                     @SuppressWarnings("unused")  
                     @Override  
                     public void run() {  
                         for (int i = 0; i < 500000; i++) {  
                             Integer crunchifyRandomNumber = (int) Math.ceil(Math.random() * 550000);  
                             // Retrieve value. We are not using it anywhere  
                             Integer crunchifyValue = crunchifyThreads.get(String.valueOf(crunchifyRandomNumber));  
                             // Put value  
                            crunchifyThreads.put(String.valueOf(crunchifyRandomNumber), crunchifyRandomNumber);  
                         }  
                     }  
                 });  
             }  

             // Make sure executor stops  
            crunchifyExServer.shutdown();  
             // Blocks until all tasks have completed execution after a shutdown request  
             crunchifyExServer.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);  
           long entTime = System.nanoTime();  
            long totalTime = (entTime - startTime) / 1000000L;  
             averageTime += totalTime;  
             System.out.println("2500K entried added/retrieved in " + totalTime + " ms");  
         }  
         System.out.println("For " + crunchifyThreads.getClass() + " the average time is " + averageTime / 5 + " ms\n");  
     }  
 }

结果显示,ConcurrentHashMap性能是明显优于Hashtable和SynchronizedMap的,ConcurrentHashMap花费的时间比前两个的一半还少。

原文地址:

https://www.cnblogs.com/shamo89/p/6700353.html

原文发布于微信公众号 - java技术学习之道(javajsxxzd)

原文发表时间:2018-04-22

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏和蔼的张星的图像处理专栏

数据结构栈队列链表树二叉查找树

这学期刚回到所里的时候把c++数据结构看了一遍,基本的数据结构照着视频也敲了一遍,不过那个时候自己对c++的了解只限于一些基本的语法,c++primer也还没有...

1544
来自专栏小鹏的专栏

tf API 研读4:Inputs and Readers

tensorflow中数据的读入相关类或函数: 占位符(Placeholders) tf提供一种占位符操作,在执行时需要为其提供数据data。 操作 描...

57110
来自专栏逆向技术

逆向知识十一讲,识别函数的调用约定,函数参数,函数返回值.

      逆向知识十一讲,识别函数的调用约定,函数参数,函数返回值. 在反汇编中,我们常常的会看到各种的函数调用,或者通过逆向的手段,单独的使用这个函数,那么...

2419
来自专栏和蔼的张星的图像处理专栏

69. 二叉树的层次遍历层次遍历+queue

给出一棵二叉树,返回其节点值的层次遍历(逐层从左往右访问) 样例 给一棵二叉树 {3,9,20,#,#,15,7} :

1694
来自专栏java一日一条

Python开发的10个小贴士

下面是十个Python中很有用的贴士和技巧。其中一些是初学这门语言常常会犯的错误。

612
来自专栏阿凯的Excel

Vlookup最高阶应用的全网唯一解决方案

古有烟笼寒水月笼沙的缥缈朦胧,今有查询函数的假模糊匹配的终极应用!今天分享的内容是全网唯一哦~ 为啥是假模糊匹配呢?一会和你说! 嗯嗯,Vlookup函数应该...

2755
来自专栏GIS讲堂

说说geotools中坐标转换那点事

本文说说geotools中坐标转换的那点事情,以WGS84和web墨卡托相互转换为例。

2502
来自专栏Ryan Miao

Java8-理解Collector

2864
来自专栏静默虚空的博客

[算法题] 字节流解析

字节流解析 题目标题: 根据数值占用BIT数,按顺序从输入字节流中解析出对应数值,解析顺序按输入数组astElement索引升序。 详细描述: 接口说明...

2505
来自专栏云霄雨霁

数据压缩----霍夫曼树和霍夫曼压缩

1840

扫码关注云+社区