线程安全ConcurrentHashMap源码分析

package thread;

import java.util.concurrent.ConcurrentHashMap;

/**
 * ConcurrentHashMap源码分析
 * 1、类的继承关系
 * 2、类的属性
 * sizeCtl  table的初始化和扩容需要用到的变量
 * -1 代表table正在初始化
 * -N 代表N-1个线程在进行扩容操作
 * 其他情况:
 * 1)如果table未初始化,table表示初始化的大小
 * 2)如果table初始化完成,表示table的容量,默认0.75*table.size
 *
 * 初始化操作在第一次put完成
 * concurrencyLevel在jdk1.8的意义改变,并不代表当前所允许的并发数,只是
 * 用来sizeCtl大小,在jdk1.8的并发控制针对具体的桶而言,所以有多少个桶就有
 * 多少个并发数
 * 3、构造函数
 * 只是sizeCtl初始化,表示table初始化大小

 * 4、put/get
 *   final V putVal(K key, V value, boolean onlyIfAbsent) {
    
    
 *         //ConcurrentHashMap中键和值不能为空
 *         if (key == null || value == null) throw new NullPointerException();
 *         int hash = spread(key.hashCode());
 *         int binCount = 0;
 *         for (Node<K,V>[] tab = table;;) {
    
    
 *         //无线循环 (多线程环境)
 *             Node<K,V> f; int n, i, fh;
 *             if (tab == null || (n = tab.length) == 0)
 *             //表为空或者表长度为0
 *                 //初始化表
 *                 tab = initTable();
 *             else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
    
    
 *                 //表不为空,该桶位置为空时
 *                 if (casTabAt(tab, i, null,
 *                              new Node<K,V>(hash, key, value, null)))
 *                     //CAS方式插入一个新的Node
 *                     break;                   // no lock when adding to empty bin
 *             }
 *             else if ((fh = f.hash) == MOVED)
 *                 //该节点的hash值为Moved,说明当前节点是ForwardingNode,意味着有其他线程
 *                 //在进行扩容,则一起进行扩容操作
 *                 tab = helpTransfer(tab, f);
 *             else {
 *                 V oldVal = null;
 *                 synchronized (f) {
 *                     //加锁同步,针对首个节点进行加锁操作
 *                     if (tabAt(tab, i) == f) {
 *                         //找到table表下标为i的节点
 *                         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, null);
 *                                     //尾插法插入一个新节点
 *                                     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;
 *                             }
 *                         }
 *                     }
 *                 }
 *                 if (binCount != 0) {
    
    
 *                     if (binCount >= TREEIFY_THRESHOLD)
 *                         treeifyBin(tab, i);
 *                     if (oldVal != null)
 *                         return oldVal;
 *                     break;
 *                 }
 *             }
 *         }
 *         //增加binCount容量,检查当前容量是否需要进行扩容
 *         addCount(1L, binCount);
 *         return null;
 *     }

 *
 *
 * Unsafe类是用于帮助Java访问操作系统资源的类,如分配内存、释放内存,里面
 * 全部都是native方法,通过unsafe,Java才具有了底层操作的能力
 *
 *
 * compareAndSwapInt CAS操作
 * CAS是乐观锁技术,当多个线程尝试CAS同时更新同一个变量时,只有其中一个线程能够成功
 * 更新变量的值,其他线程都失败,失败的线程并不会阻塞,而是被告知这次竞争失败,并且
 * 可以再次尝试
 * CAS操作-原子性操作
 * CAS操作逻辑:
 * 如果内存位置V的值与预期原值A相匹配,那么处理器自动更新该位置为新值B。
 * 否则处理器不会做任何操作。
 *
 *
 * 获取某一位置的元素没有直接使用table[index],而是tabAt(table, index)?
 * Java内存模型,我们知道每一个线程都有一个自己的工作内存,里面table的副本,
 * table本身用volatile,但是不能够保证线程每次拿到的table里面都是最新元素,
 * 因为volatile只能够保证引用可见,Unsafe.getObjectVolatile可以直接获取
 * 指定内存的数据,保证每次拿到的数据都是最新的
 *
 *   public V get(Object key) {
    
    
 *         Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;
 *         int h = spread(key.hashCode());
 *         if ((tab = table) != null && (n = tab.length) > 0 &&
 *             (e = tabAt(tab, (n - 1) & h)) != null) {
    
    
 *             if ((eh = e.hash) == h) {
 *                 if ((ek = e.key) == key || (ek != null && key.equals(ek)))
 *                     return e.val;
 *             }
 *             else if (eh < 0)
 *                 return (p = e.find(h, key)) != null ? p.val : null;
 *             while ((e = e.next) != null) {
 *                 if (e.hash == h &&
 *                     ((ek = e.key) == key || (ek != null && key.equals(ek))))
 *                     return e.val;
 *             }
 *         }
 *         return null;
 *     }
 * get没有加锁,如何保证读到的数据不是脏数据呢?
 * value和next用volatile修饰,在多线程环境下某一个线程A修饰节点的value或者
 * 新增节点对其他线程都是可见的
 *
 */
public class TestDemo15 {
    
    
    public static void main(String[] args) {
    
    
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_47198561/article/details/114490706