Java集合——HashMap,HashTable,ConcurrentHashMap区别

 

 

Map:“键值”对映射的抽象接口。该映射不包括重复的键,一个键对应一个值。

 

SortedMap:有序的键值对接口,继承Map接口。

 

NavigableMap:继承SortedMap,具有了针对给定搜索目标返回最接近匹配项的导航方法的接口。

 

AbstractMap:实现了Map中的绝大部分函数接口。它减少了“Map的实现类”的重复编码。

 

Dictionary:任何可将键映射到相应值的类的抽象父类。目前被Map接口取代。

 

TreeMap:有序散列表,实现SortedMap 接口,底层通过红黑树实现。

 

HashMap:是基于“拉链法”实现的散列表。底层采用“数组+链表”实现。

 

WeakHashMap:基于“拉链法”实现的散列表。

 

HashTable:基于“拉链法”实现的散列表。

 

 

一 、HashMap

Java集合类是个非常重要的知识点,HashMap、HashTable、ConcurrentHashMap等算是集合类中的重点

  1、HashMap是非线程安全的,HashTable是线程安全的。

  2、HashMap的键和值都允许有null值存在,而HashTable则不行。

  3、因为线程安全的问题,HashMap效率比HashTable的要高。

  4、HashTable 基于synchronized锁,读写只能单线程访问,阻塞保证读写一致,多线程高并发下阻塞严重、效率低下

  4、ConcurrentHashMap 高并发的、效率高,基于lock锁, concurrentLevel划分出了多个Segment来对key-value进行存储,默认16个线程无阻赛

    缺点:有可能读写不一致,不一定能保证数据的完整性

 

Java中的另一个线程安全的与HashMap极其类似的类是什么?同样是线程安全,它与HashTable在线程同步上有什么不同?

 

 

二、HashTable的内部存储结构

 

HashTable和HashMap采用相同的存储机制,二者的实现基本一致,不同的是:

 

  1、HashMap是非线程安全的,HashTable是线程安全的,内部的方法基本都是synchronized。

 

  2、HashTable不允许有null值的存在。

 

在HashTable中调用put方法时,如果key为null,直接抛出NullPointerException。其它细微的差别还有,比如初始化Entry数组的大小等等,但基本思想和HashMap一样。

 

 

 

三、HashTable和ConcurrentHashMap的比较

 

ConcurrentHashMap是线程安全的HashMap的实现。同样是线程安全的类,它与HashTable在同步方面有什么不同呢?

 

synchronized关键字加锁的原理,其实是对对象加锁,不论你是在方法前加synchronized还是语句块前加,锁住的都是对象整体

但是ConcurrentHashMap的同步机制和这个不同,它不是加synchronized关键字,而是基于lock操作的,这样的目的是保证同步的时候,锁住的不是整个对象。

事实上,ConcurrentHashMap可以满足concurrentLevel个线程并发无阻塞的操作集合对象。

ConcurrentHashMap基于concurrentLevel划分出了多个Segment来对key-value进行存储,从而避免每次锁定整个数组,在默认的情况下,允许16个线程并发无阻塞的操作集合对象,尽可能地减少并发时的阻塞现象。

 

在多线程的环境中,相对于HashTable,ConcurrentHashMap会带来很大的性能提升!

 

 

 

简单说下 synchronized 和lock的区别

 

synchronized是java中的一个关键字,也就是说是Java语言内置的特性。那么为什么会出现Lock呢?

  在上面一篇文章中,我们了解到如果一个代码块被synchronized修饰了,当一个线程获取了对应的锁,并执行该代码块时,其他线程便只能一直等待,等待获取锁的线程释放锁,
而这里获取锁的线程释放锁只会有两种情况:   1)获取锁的线程执行完了该代码块,然后线程释放对锁的占有;   2)线程执行发生异常,此时JVM会让线程自动释放锁。   那么如果这个获取锁的线程由于要等待IO或者其他原因(比如调用sleep方法)被阻塞了,但是又没有释放锁,其他线程便只能干巴巴地等待,试想一下,这多么影响程序执行效率。   因此就需要有一种机制可以不让等待的线程一直无期限地等待下去(比如只等待一定的时间或者能够响应中断),通过Lock就可以办到。   再举个例子:当有多个线程读写文件时,读操作和写操作会发生冲突现象,写操作和写操作会发生冲突现象,但是读操作和读操作不会发生冲突现象。   但是采用synchronized关键字来实现同步的话,就会导致一个问题:   如果多个线程都只是进行读操作,所以当一个线程在进行读操作时,其他线程只能等待无法进行读操作。   因此就需要一种机制来使得多个线程都只是进行读操作时,线程之间不会发生冲突,通过Lock就可以办到。   另外,通过Lock可以知道线程有没有成功获取到锁。这个是synchronized无法办到的。   总结一下,也就是说Lock提供了比synchronized更多的功能。但是要注意以下几点:   1)Lock不是Java语言内置的,synchronized是Java语言的关键字,因此是内置特性。Lock是一个类,通过这个类可以实现同步访问;   2)Lock和synchronized有一点非常大的不同,采用synchronized不需要用户去手动释放锁,当synchronized方法或者synchronized代码块执行完之后,系统会自动让线程释放对锁的占用;
    而Lock则必须要用户去手动释放锁,如果没有主动释放锁,就有可能导致出现死锁现象。

 

  

 

 

posted @ 2017-11-22 16:19  低调人生  阅读(1299)  评论(0编辑  收藏  举报