Java基础系列(面试必备):HashMap,Hashtable(HashTable),SynchronizedMap和ConcurrentHashMap !
前言
今天博主将为大家分享:Java基础系列(面试必备):HashMap,Hashtable(HashTable),SynchronizedMap和ConcurrentHashMap !不喜勿喷,如有异议欢迎讨论!
首先为什么我写的是Hashtable不是HashTable大家可以自行百度一下,这个是java 1.0带过来的东西,不符合驼峰命名法但是他们也懒得修复删除了
首先
HashMap线程安全的吗?
Java中平时用的最多的Map集合就是HashMap了,它是线程不安全的。
例如:
-
当用在方法内的局部变量时,局部变量属于当前线程级别的变量,其他线程访问不了,所以这时也不存在线程安全不安全的问题了。
-
当用在单例对象成员变量的时候呢?这时候多个线程过来访问的就是同一个HashMap了,对同个HashMap操作这时候就存在线程安全的问题了。
线程安全的Map
为了避免出现场景2的线程安全的问题,不能使用HashMap作为成员变量,要寻求使用线程安全的Map,下面来总结下有哪些线程安全的Map
Hashtable(HashTable)
private Map<String, Object> map = new Hashtable<>();
我们来看一下Hashtable的get()和put()的源码
@SuppressWarnings("unchecked")
public synchronized V get(Object key) {
Entry<?,?> tab[] = table;
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
if ((e.hash == hash) && e.key.equals(key)) {
return (V)e.value;
}
}
return null;
}
public synchronized V put(K key, V value) {
// Make sure the value is not null
if (value == null) {
throw new NullPointerException();
}
// Makes sure the key is not already in the hashtable.
Entry<?,?> tab[] = table;
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
@SuppressWarnings("unchecked")
Entry<K,V> entry = (Entry<K,V>)tab[index];
for(; entry != null ; entry = entry.next) {
if ((entry.hash == hash) && entry.key.equals(key)) {
V old = entry.value;
entry.value = value;
return old;
}
}
addEntry(hash, key, value, index);
return null;
}
通过上述代码可以看到HashTable的get/put方法都被synchronized关键字修饰,说明它们是方法级别阻塞的,它们占用共享资源锁,所以导致同时只能一个线程操作get或者put,而且get/put操作不能同时执行,所以这种同步的集合效率非常低,一般不建议使用这个集合。
SynchronizedMap
private Map<String, Object> map = Collections.synchronizedMap(new HashMap<String, Object>());
这种是直接使用工具类里面的方法创建SynchronizedMap,把传入进行的HashMap对象进行了包装同步而已,来看看它的源码。
/**
* @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();}
}
private transient Set<K> keySet;
private transient Set<Map.Entry<K,V>> entrySet;
private transient Collection<V> values;
public Set<K> keySet() {
synchronized (mutex) {
if (keySet==null)
keySet = new SynchronizedSet<>(m.keySet(), mutex);
return keySet;
}
}
public Set<Map.Entry<K,V>> entrySet() {
synchronized (mutex) {
if (entrySet==null)
entrySet = new SynchronizedSet<>(m.entrySet(), mutex);
return entrySet;
}
}
public Collection<V> values() {
synchronized (mutex) {
if (values==null)
values = new SynchronizedCollection<>(m.values(), mutex);
return values;
}
}
public boolean equals(Object o) {
if (this == o)
return true;
synchronized (mutex) {return m.equals(o);}
}
public int hashCode() {
synchronized (mutex) {return m.hashCode();}
}
public String toString() {
synchronized (mutex) {return m.toString();}
}
贴出来一部分代码太长了这个
通过上述代码看到这个同步方式实现也比较简单,看出SynchronizedMap的实现方式是加了个对象锁,每次对HashMap的操作都要先获取这个mutex的对象锁才能进入,所以性能也不会比HashTable好到哪里去,也不建议使用。
ConcurrentHashMap
这个也是最推荐使用的线程安全的Map,也是实现方式最复杂的一个集合,每个版本的实现方式也不一样,在jdk8之前是使用分段加锁的一个方式,分成16个桶,每次只加锁其中一个桶,而在jdk8又加入了红黑树和CAS算法来实现。
Map<String, Object> map = new ConcurrentHashMap<>();
下面看一下ConcurrentHashMap的get()方法
public V get(Object key) {
Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;
int h = spread(key.hashCode());
if ((tab = table) != null && (n = tab.length) > 0 &&
(e = tabAt(tab, (n - 1) & h)) != null) {
if ((eh = e.hash) == h) {
if ((ek = e.key) == key || (ek != null && key.equals(ek)))
return e.val;
}
else if (eh < 0)
return (p = e.find(h, key)) != null ? p.val : null;
while ((e = e.next) != null) {
if (e.hash == h &&
((ek = e.key) == key || (ek != null && key.equals(ek))))
return e.val;
}
}
return null;
}
通过上述代码可以看到虽然实现起来很复杂,但使用起来也是非常简单的,在java面试中问的频率也非常高,最重要的是性能要比上面两种同步方式要快太多,推荐使用。后面也会去学习ConcurrentHashMap 发布一些文章
到这里:Java基础系列(面试必备):HashMap,Hashtable(HashTable),SynchronizedMap和ConcurrentHashMap !分享完毕了,快去试试吧!
最后
-
更多参考精彩博文请看这里:《陈永佳的博客》
-
喜欢博主的小伙伴可以加个关注、点个赞哦,持续更新嘿嘿!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/97796.html