相信大家都使用过HashMap、ArrayList等数据结构,在非并发的情况下,它们都能很好的工作,但是一到并发环境,就可能出现各种各样的问题。这是为什么呢?当然,它们是非线程安全的,大家都知道。为什么它们是线程不安全的呢?在并发环境中,我们应该使用什么容器呢?在这里先以HashMap简单介绍。
并发下的HashMap
在并发环境中,我们来看看使用同一个HashMap会出现什么问题呢?示例如下:
public class HashMapDemo {
static HashMap map = new HashMap();
public static void main(String[] args) throws InterruptedException {
Thread one = new Thread(new HashMapThread(0));
Thread two = new Thread(new HashMapThread(1));
one.start();
two.start();
one.join();
two.join();
System.out.println(map.size());
}
}
class HashMapThread implements Runnable {
int i;
public HashMapThread(int i) {
this.i = i;
}
@Override
public void run() {
for (int j = i; j < 10000; j += 2) {
map.put(Integer.toString(j), Integer.toHexString(j));
}
}
}
这是一个非常简单的操作HashMap的并发测试,从上述代码来看,我们的预期结果应该是10000,然而实际上的结果确并非如此,本人在JDK1.8的环境下运行,得到过如下几种结果:
1.程序运行正常,结果<10000。
2.程序出现异常,java.lang.ClassCastException: java.util.HashMap$Node cannot be cast to java.util.HashMap$TreeNode。
至于结果为10000,我这里一次都没有得到过,汗颜...。这是为什么呢?不应该是10000吗?当然,我也非常好奇,所以看了一下put源码,看看究竟为何。
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
//判断table是否为空
if ((tab = table) == null || (n = tab.length) == 0)
//如果为空,进行扩容&#x