非线程安全的HashMap 和 线程安全的ConcurrentHashMap
在平时开发中,我们经常采用HashMap来作为本地缓存的一种实现方式,将一些如系统变量等数据量比较少的参数保存在HashMap中,并将其作为单例类的一个属性。在系统运行中,使用到这些缓存数据,都可以直接从该单例中获取该属性集合。但是,最近发现,HashMap并不是线程安全的,如果你的单例类没有做代码同步或对象锁的控制,就可能出现异常。
首先看下在多线程的访问下,非现场安全的HashMap的表现如何,在网上看了一些资料,自己也做了一下测试:
public class MainClass {2
3
public static final HashMap<String, String> firstHashMap=new HashMap<String, String>();4
5
public static void main(String[] args) throws InterruptedException {6
7
//线程一8
Thread t1=new Thread(){9
public void run() {;i++){
firstHashMap.put(String.valueOf(i), String.valueOf(i));
}
}
};
//线程二
Thread t2=new Thread(){
public void run() {;j++){
firstHashMap.put(String.valueOf(j), String.valueOf(j));
}
}
};
t1.start();
t2.start();
//主线程休眠1秒钟,以便t1和t2两个线程将firstHashMap填装完毕。);
;l++){
//如果key和value不同,说明在两个线程put的过程中出现异常。
if(!String.valueOf(l).equals(firstHashMap.get(String.valueOf(l)))){
System.err.println(String.valueOf(l)+":"+firstHashMap.get(String.valueOf(l)));
}
}
}
}上面的代码在多次执行后,发现表现很不稳定,有时没有异常文案打出,有时则有个异常出现:
为什么会出现这种情况,主要看下HashMap的实现:
public V put(K key, V value) {2
if (key == null)3
return putForNullKey(value);4
int hash = hash(key.hashCode());5
int i = indexFor(hash, table.length);6
for (Entry<K,V> e = table[i]; e != null; e = e.next) {7
Object k;8
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {9
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(hash, key, value, i);
return null;
}我觉得问题主要出现在方法addEntry,继续看:
void addEntry(int hash, K key, V value, int bucketIndex) {
Entry<K,V> e = table[bucketIndex];
table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
if (size++ >= threshold)* table.length);
}从代码中,可以看到,如果发现哈希表的大小超过阀值threshold,就会调用resize方法,扩大容量为原来的两倍,而扩大容量的做法是新建一个Entry[]:
void resize(int newCapacity) {2
Entry[] oldTable = table;3
int oldCapacity = oldTable.length;4
if (oldCapacity == MAXIMUM_CAPACITY) {5
threshold = Integer.MAX_VALUE;6
return;7
}8

9
Entry[] newTable = new Entry[newCapacity];
transfer(newTable);
table = newTable;
threshold = (int)(newCapacity * loadFactor);
}一般我们声明HashMap时,使用的都是默认的构造方法:HashMap<K,V>,看了代码你会发现,它还有其它的构造方法:HashMap(intinitialCapacity, float loadFactor),其中参数initialCapacity为初始容量,loadFactor为加载因子,而之前我们看到的threshold
= (int)(capacity* loadFactor);如果在默认情况下,一个HashMap的容量为16,加载因子为0.75,那么阀值就是12,所以在往HashMap中put的值到达12时,它将自动扩容两倍,如果两个线程同时遇到HashMap的大小达到12的倍数时,就很有可能会出现在将oldTable转移到newTable的过程中遇到问题,从而导致最终的HashMap的值存储异常。
JDK1.0引入了第一个关联的集合类HashTable,它是线程安全的。 HashTable的所有方法都是同步的。
JDK2.0引入了HashMap,它提供了一个不同步的基类和一个同步的包装器synchronizedMap。synchronizedMap被称为有条件的线程安全类。
JDK5.0util.concurrent包中引入对Map线程安全的实现ConcurrentHashMap,比起synchronizedMap,它提供了更高的灵活性。同时进行的读和写操作都可以并发地执行。
所以在开始的测试中,如果我们采用ConcurrentHashMap,它的表现就很稳定,所以以后如果使用Map实现本地缓存,为了提高并发时的稳定性,还是建议使用ConcurrentHashMap。
====================================================================
另外,还有一个我们经常使用的ArrayList也是非线程安全的,网上看到的有一个解释是这样:
一个 ArrayList 类,在添加一个元素的时候,它可能会有两步来完成:1. 在 Items[Size] 的位置存放此元素;2.增大 Size 的值。
在单线程运行的情况下,如果 Size = 0,添加一个元素后,此元素在位置 0,而且 Size=1;
而如果是在多线程情况下,比如有两个线程,线程 A 先将元素存放在位置 0。但是此时 CPU 调度线程A暂停,线程 B得到运行的机会。线程B也将元素放在位置0,(因为size还未增长),完了之后,两个线程都是size++,结果size变成2,而只有items[0]有元素。
util.concurrent包也提供了一个线程安全的ArrayList替代者CopyOnWriteArrayList。
本文转载自:http://www.blogjava.net/lukangping/articles/331089.html
非线程安全的HashMap 和 线程安全的ConcurrentHashMap的更多相关文章
- 如何使用线程安全的HashMap
转载:https://blog.csdn.net/qq_31493821/article/details/78855069 HashMap为什么线程不安全 导致HashMap线程不安全的原因可能有两种 ...
- Java 非线程安全的HashMap如何在多线程中使用
Java 非线程安全的HashMap如何在多线程中使用 HashMap 是非线程安全的.在多线程条件下,容易导致死循环,具体表现为CPU使用率100%.因此多线程环境下保证 HashMap 的线程安全 ...
- HashMap为什么线程不安全(hash碰撞与扩容导致)
一直以来都知道HashMap是线程不安全的,但是到底为什么线程不安全,在多线程操作情况下什么时候线程不安全? 让我们先来了解一下HashMap的底层存储结构,HashMap底层是一个Entry数组,一 ...
- HashMap变成线程安全方法
我们都知道.HashMap是非线程安全的(非同步的).那么怎么才能让HashMap变成线程安全的呢? 我认为主要可以通过以下三种方法来实现: 1.替换成Hashtable,Hashtable通过对整个 ...
- 为什么说 HashMap 是非线程安全的?
我们在学习 HashMap 的时候,都知道 HashMap 是非线程安全的,同时我们知道 HashTable 是线程安全的,因为里面的方法使用了 synchronized 进行同步. 但是 HashM ...
- Java基础知识强化之集合框架笔记80:HashMap的线程不安全性的体现
1. HashMap 的线程不安全性的体现: 主要是下面两方面: (1)多线程环境下,多个线程同时resize()时候,容易产生死锁现象.即:resize死循环 (2)如果在使用迭代器的过程中有其他线 ...
- 浅谈HashMap与线程安全 (JDK1.8)
HashMap是Java程序员使用频率最高的用于映射(键值对)处理的数据类型.HashMap 继承自 AbstractMap 是基于哈希表的 Map 接口的实现,以 Key-Value 的形式存在,即 ...
- [集合]线程安全的HashMap
一.一般模式下线程安全的HashMap 默认情况常用的HashMap都是线程不安全的,在多线程的环境下使用,常常会造成不可预知的,莫名其妙的错误.那么,我们如何实现一个线程安全的HashMap呢?其中 ...
- hashMap的线程不安全
hashMap是非线程安全的,表现在两种情况下: 1 扩容: t1线程对map进行扩容,此时t2线程来读取数据,原本要读取位置为2的元素,扩容后此元素位置未必是2,则出现读取错误数据. 2 hash碰 ...
随机推荐
- 使用svn时出现Can't switch /XXX/XXX because it is not the repository yet
问题描述 出现的问题如题目所示. 翻译一下:不能选择这个目录,原因是这个目录还不是svn仓库. 解决办法 这个问题出现的原因是你将项目中的.svn文件夹删除了,一般情况你是可以通过撤销将文件恢复的.如 ...
- 2.3 使用Android Studio 简单设计UI界面
首先 创建一个新的项目找到app 文件目录下的layout的 activity_main.xml 因为Android Studio 是可视化的,所有操作都可以在图形界面进行. 该res 界面当中 d ...
- 对比Node.js和Python 帮你确定理想编程解决方案!
世上没有最好的编程语言.有些编程语言比其他编程语言用于更具体的事情.比如,你可能需要移动应用程序,网络应用程序或更专业化的系统,则可能会有特定的语言.但是我们暂时假设你需要的是一个相对来说比较简单的网 ...
- 微信小程序之组件常见的问题
小程序自定义的组件: (1)组件的结构 自定义的组件和普通的页面定义没有什么区别,也包含了四个文件:xxx.wxml(组件的视图层),xxx.json,xxx.js,xxx.wxss 组件的xxx.w ...
- CSS 选择器权重计算规则(转)
其实,CSS有自己的优先级计算公式,而不仅仅是行间>内部>外部样式:ID>class>元素. 一.样式类型 1.行间 <h1 style="font-size: ...
- 计算机网络(4): socket select使用:聊天室模版
知识点: 如上所示,用户首先将需要进行IO操作的socket添加到select中,然后阻塞等待select系统调用返回.当数据到达时,socket被激活,select函数返回.用户线程正式发起read ...
- 一维跳棋(BFS)
一维跳棋是一种在1×(2N+1) 的棋盘上玩的游戏.一共有N个棋子,其中N 个是黑的,N 个是白的.游戏开始前,N 个白棋子被放在一头,N 个黑棋子被放在另一头,中间的格子空着. 在这个游戏里有两种移 ...
- 操作实践,git本地分支执行rebase,让主干分支记录更简洁
声明:迁移自本人CSDN博客https://blog.csdn.net/u013365635 我们平时在写代码的时候,难免会修修改改,如果团队中每个人的代码提交记录都包含着一堆中间过程,是很不利于团队 ...
- PAT Advanced 1029 Median (25) [two pointers]
题目 Given an increasing sequence S of N integers, the median is the number at the middle position. Fo ...
- UML-类图-关键字如何使用?
部分预定义UML关键字: 例如: 加上关键字,明确,清晰.