Questions 04 (并发问题总结)
Questions 04
一、并发问题总结
Reference: JavaGuide
1. Java内存模型(Java Memory Model)的理解
注意不是运行时数据区(Java Runtime Area)
-
Java内存模型抽象了线程和主内存之间的关系,就比如说线程之间的共享变量必须存储在主内存中。Java内存模型主要目的是为了屏蔽系统和硬件的差异,避免一套代码在不同的平台下产生的效果不一致。
-
JMM数据不一致问题:
在JDK1.2之前,Java的内存模型实现总是从主内存(即共享内存)读取变量,是不需要进行特别的注意的。而在当前的Java内存模型下,线程可以把变量保存本地内存(比如机器的寄存器)中,而不是直接在主内存中进行读写。这就可能造成一个线程在主内存中修改了一个变量的值,而另外一个线程还继续使用它在寄存器中的变量值的副本,造成数据的不一致。
主内存:所有线程创建的实例对象都存放在主内存中,不管该实例对象是成员变量还是方法中的局部变量。
本地内存:每个线程都有一个私有的本地内存来存储共享变量的副本,并且每个线程只能访问自己的本地内存,无法访问其他线程的本地内存。本地内存是JMM抽象出来的一个概念,存储了主内存中的共享变量副本。
JMM数据不一致问题解决:
将变量声明为volatile,这就指示JVM这个变量是共享且不稳定的,每次使用它都到主存中进行读取。所以volatile关键字除了防止 JVM的指令重排 ,还有一个重要的作用就是保证变量的可见性。
并发编程可见性:当一个线程对共享变量进行了修改,那么另外的线程都是立即可以看到修改后的最新值。volatile关键字可以保证共享变量的可见性。
2. ConcurrentHashMap的put源码分析
-
主要流程
- 根据key计算出hashcode。
- 判断是否需要进行初始化。
- 即为当前 key 定位出的 Node,如果为空表示当前位置可以写入数据,利用 CAS 尝试写入,失败则自旋保证成功。
- 如果当前位置的
hashcode == MOVED == -1,则需要进行扩容。 - 如果都不满足,则利用synchronized锁写入数据。
- 如果数量大于
TREEIFY_THRESHOLD则要执行树化方法,在treeifyBin中会首先判断当前数组长度≥64时才会将链表转换为红黑树。
-
Code
public V put(K key, V value) { return putVal(key, value, false); } /** Implementation for put and putIfAbsent */ final V putVal(K key, V value, boolean onlyIfAbsent) { // key 和 value 不能为空 if (key == null || value == null) throw new NullPointerException(); int hash = spread(key.hashCode()); int binCount = 0; for (Node<K,V>[] tab = table;;) { // f = 目标位置元素 Node<K,V> f; int n, i, fh; K fk; V fv;// fh 后面存放目标位置的元素 hash 值 // 分支1:整个数组初始化 if (tab == null || (n = tab.length) == 0) // 数组桶为空,初始化数组桶(自旋+CAS) tab = initTable(); // 分支2:第i个元素初始化 else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) { // 桶内为空,CAS 放入,不加锁,成功了就直接 break 跳出 if (casTabAt(tab, i, null, new Node<K,V>(hash, key, value))) break; // no lock when adding to empty bin } // 分支3:扩容 else if ((fh = f.hash) == MOVED) tab = helpTransfer(tab, f); else if (onlyIfAbsent // check first node without acquiring lock && fh == hash && ((fk = f.key) == key || (fk != null && key.equals(fk))) && (fv = f.val) != null) return fv; // 分支4:放入元素 else { V oldVal = null; // 使用 synchronized 加锁加入节点 synchronized (f) { if (tabAt(tab, i) == f) { // 说明是链表 if (fh >= 0) { binCount = 1; // 循环加入新的或者覆盖节点 for (Node<K,V> e = f;; ++binCount) { K ek; if (e.hash == hash && ((ek = e.key) == key || (ek != null && key.equals(ek)))) { oldVal = e.val; if (!onlyIfAbsent) e.val = value; break; } Node<K,V> pred = e; if ((e = e.next) == null) { pred.next = new Node<K,V>(hash, key, value); break; } } } else if (f instanceof TreeBin) { // 红黑树 Node<K,V> p; binCount = 2; if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key, value)) != null) { oldVal = p.val; if (!onlyIfAbsent) p.val = value; } } else if (f instanceof ReservationNode) throw new IllegalStateException("Recursive update"); } } // 如果是链表,上面的binCount会一直累加 if (binCount != 0) { if (binCount >= TREEIFY_THRESHOLD) treeifyBin(tab, i);// 超出阈值,转换为红黑树 if (oldVal != null) return oldVal; break; } } } addCount(1L, binCount);// 总元素个数累加1 return null; }

浙公网安备 33010602011771号