并发容器类-Conconcurrent容器原理

本文深入探讨了Java并发容器,包括HashMap的1.8结构、初始化及存储原理,ConcurrentHashMap在JDK1.7和1.8的实现方式,以及其线程安全策略。同时,提到了ConcurrentSkipListMap的跳跃查找机制和List类型的CopyOnWriteArrayList。文章还简述了Set、Queue的实现原理,并举例说明了非阻塞队列和阻塞队列的实现方式。
摘要由CSDN通过智能技术生成

HashMap

数据结构:数组{链表,链表,链表}
JDK1.8之后对于HashMap进行了优化

1.8结构

初始时:数组{链表,链表,链表}
当链表增长到8个元素时链表转化为红黑树:数组{链表(–>红黑树)链表(–>红黑树)}

HashMap初始化

hashMap初始化的时候会构建存储大小的阈值和扩容临界值,当hashmap大小接近阈值时,就会进行扩容,hashmap的大小是2的幂数,所以扩容也是按照当前大小×2进行扩容。如new HashMap大小是519时,那么分配给他的内存就是1024

HashMap的存储

在这里插入图片描述
当一个<k,v>键值对需要存储的时候,先通过hash计算取模(),根据取模结果存入数组的相应位置。

冲突:当有两个<k,v>取模结果相同时,就要存入相同的数组位置,而数组的这个位置上已经有一个<k,v>。
在这里插入图片描述
当数组位置冲突时会转化成一个链表,第二个<k,v>就会存在第一个<k,v>后。当链表达到一定长度时,数组就会进行扩容(链表的数据结构特性是易存不易查,长度越长查询越慢)。
红黑树实现不做描述了,java实现红黑树很繁琐这里不贴代码了。红黑树的转换原理是当单个链表达到8那么这个链表就转化成红黑树,而不会转化数组中其他链表。

ConcurrentHashMap

JDK1.7底层:数组{数组segment{entry,entry},数组segment{entry},…}
JDK1.8底层:数组{链表}

JDK1.7ConcurrentHashMap操作原理

在1.7时ConcurrentHashMap不会扩容,初始化时就已经确定了segement的数量,多线程并行操作ConcurrentHashMap时每个线程操作一个segment数组,当线程操作segment时会lock这个segment,所以ConcurrentHashMap是安全的。segment的作用相当于一个加锁(分段锁模式)的hashMap,这个hashMap不会又转化红黑树的操作,segment是可以扩容的。
在这里插入图片描述

JDK1.8ConcurrentHashMap操作原理

JDK1.8弃用了segment,使用node<k,v>来实现数组内部元素。
在这里插入图片描述

node元素初始化

node初始化的时候,会有一个位置信息的控制权(SIZECTL),当有线程的hash值=node元素位置,而这个数组的node节点刚好为空,那么需要所有要操作这个node元素的线程争抢SIZECTL,只有一个线程可以抢到,所以只有一个线程可以操作node,这个过程使用了CAS自旋操作,但是并不会消耗大量的性能,因为没有抢到SIZECTL的线程会使用Thread.yield()方法来释放资源,当线程1初始化node完成之后,其他线程则不会直接break跳出初始化。

node位置冲突时的解决办法

当两个key的hash值都对应一个node时,node只允许一个key进行put操作,此时会使用synchronized的锁住这个node链表的head节点,另一个key的线程则等待(链表是key1–next–>key2–next–>key3所以锁住head之后相当于锁住整个链表,因为无法从头遍历)。当链表达到一定数量之后还是会转化成红黑树,每次put元素后都会比对阈值,当达到阈值时就会执行扩容。扩容时会使用CAS机制来保证线程的安全性。

JDK1.8ConcurrentHashMap使用锁总结

当put时,如果key1、key2不发生冲突,则使用CAS去初始化node。如果key1、key2冲突,则使用synchronized同步代码块来锁定node的head节点。

ConcurrentSkipListMap跳表

ConcurrentSkipListMap的实现原理:有序链表;无锁;value不能为空;层级越高跳跃性越大,数据越少,查询理论变快。
查找数据时,按照从上到下,从左到右顺序查找,是空间换时间,类似数据库索引的概念,在一些开元组件中有使用(level DB、redis)
插入数据的底层实现原理:首先比对key(这个key不是Map里的key,而是节点)值,对于key进行排序,然后将key插入到指定位置如下图
在这里插入图片描述
每个node都有机会升级为索引,升级原则为随机生成。
新的node是否抽取出来作为index,随机决定;index对应的level也有随机数决定。每层元素headindex固定为所有node中最小的;
索引内部属性如下图node(当前节点)、right(右侧节点)、down(下层节点)
在这里插入图片描述
整个ConcurrentSkipListMap只有一个入口就是链表的head index入口
链表实现map

public class SkipMapDemo {
   
    Node head = null; // 队列的头部

    public void add(Node node) {
   
        if (head == null) {
   
            head = node;
            return;
        }
        // 追加到队列尾部
        Node temp = head;
        while (temp.next != null) {
    // 找到一个next为空的节点,这个节点就是最后一个
            temp = temp.next;
        }
        temp.next = node;
    }

    public void print() {
    // 打印链表内容
        Node temp = head;
        while (temp.next != null) {
   
            System.out.println(temp.key);
            temp = temp.next;
        }
        System.out.println(temp.key);
    }

    public static void main(String[] args) throws IOException {
   
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值