在平时开发中,我们经常采用HashMap来作为本地缓存的一种实现方式,将一些如系统变量等数据量比较少的参数保存在HashMap中,并将其作 为单例类的一个属性。在系统运行中,使用到这些缓存数据,都可以直接从该单例中获取该属性集合。但是,最近发现,HashMap并不是线程安全的,如果你 的单例类没有做代码同步或对象锁的控制,就可能出现异常。
首先看下在多线程的访问下,非现场安全的HashMap的表现如何,在网上看了一些资料,自己也做了一下测试:
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/1fa987a29c6482f53d401256f96355eb.gif)
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/97e794c86028c5f5b5461ae5ef440a4c.gif)
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/97e794c86028c5f5b5461ae5ef440a4c.gif)
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/97e794c86028c5f5b5461ae5ef440a4c.gif)
10
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/97e794c86028c5f5b5461ae5ef440a4c.gif)
11
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
12
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/ecedf933ec37d714bd4c2545da43add2.gif)
13
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/ecedf933ec37d714bd4c2545da43add2.gif)
14
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/ecedf933ec37d714bd4c2545da43add2.gif)
15
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
16
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
17
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/97e794c86028c5f5b5461ae5ef440a4c.gif)
18
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/97e794c86028c5f5b5461ae5ef440a4c.gif)
19
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/97e794c86028c5f5b5461ae5ef440a4c.gif)
20
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
21
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/ecedf933ec37d714bd4c2545da43add2.gif)
22
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/ecedf933ec37d714bd4c2545da43add2.gif)
23
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/ecedf933ec37d714bd4c2545da43add2.gif)
24
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
25
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
26
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
27
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
28
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
29
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
30
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
31
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/97e794c86028c5f5b5461ae5ef440a4c.gif)
32
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
33
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/97e794c86028c5f5b5461ae5ef440a4c.gif)
34
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
35
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/ecedf933ec37d714bd4c2545da43add2.gif)
36
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/ecedf933ec37d714bd4c2545da43add2.gif)
37
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
38
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/ecedf933ec37d714bd4c2545da43add2.gif)
39
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
40
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/8f1ba5b45633e9678d1db480c16cae3f.gif)
上面的代码在多次执行后,发现表现很不稳定,有时没有异常文案打出,有时则有个异常出现:
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/f284b72432424ffedc8b076b0bf35302.gif)
为什么会出现这种情况,主要看下HashMap的实现:
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/1fa987a29c6482f53d401256f96355eb.gif)
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/97e794c86028c5f5b5461ae5ef440a4c.gif)
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/97e794c86028c5f5b5461ae5ef440a4c.gif)
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
10
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
11
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
12
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
13
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/ecedf933ec37d714bd4c2545da43add2.gif)
14
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/ecedf933ec37d714bd4c2545da43add2.gif)
15
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
16
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
17
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
18
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
19
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/8f1ba5b45633e9678d1db480c16cae3f.gif)
我觉得问题主要出现在方法addEntry,继续看:
1
void
addEntry(
int
hash, K key, V value,
int
bucketIndex)
{
2
Entry<K,V> e = table[bucketIndex];
3
table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
4
if (size++ >= threshold)
5
resize(2 * table.length);
6
}
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/1fa987a29c6482f53d401256f96355eb.gif)
2
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
3
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
4
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
5
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
6
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/8f1ba5b45633e9678d1db480c16cae3f.gif)
从代码中,可以看到,如果发现哈希表的大小超过阀值threshold,就会调用resize方法,扩大容量为原来的两倍,而扩大容量的做法是新建一个 Entry[]:
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/1fa987a29c6482f53d401256f96355eb.gif)
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/97e794c86028c5f5b5461ae5ef440a4c.gif)
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/ecedf933ec37d714bd4c2545da43add2.gif)
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
10
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
11
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
12
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/d18c02628675d0a2c816449d98bda930.gif)
13
![非线程安全的HashMap <wbr>和 <wbr>线程安全的ConcurrentHashMap](https://i-blog.csdnimg.cn/blog_migrate/8f1ba5b45633e9678d1db480c16cae3f.gif)
一般我们声明HashMap时,使用的都是默认的构造方法:HashMap<K,V>,看了代码你会发现,它还有其它的构造方法: HashMap(int initialCapacity, 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