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源码分析

  • 主要流程

    1. 根据key计算出hashcode。
    2. 判断是否需要进行初始化。
    3. 即为当前 key 定位出的 Node,如果为空表示当前位置可以写入数据,利用 CAS 尝试写入,失败则自旋保证成功。
    4. 如果当前位置的 hashcode == MOVED == -1,则需要进行扩容。
    5. 如果都不满足,则利用synchronized锁写入数据。
    6. 如果数量大于 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;
    }
    
posted @ 2022-04-23 17:03  Ramentherapy  阅读(86)  评论(0)    收藏  举报