第四课 集合

1 java.util包下的集合

结构图如下,黄线是继承,绿线是实现,还有就是有标记生成,意思是某种集合通过某个方法可以取得另外一种集合

在这里插入图片描述

在这里插入图片描述

1.1 List

1.1.1 ArrayList

(1)数据结构
底层采用数组

(2)特点
【1】ArrayList允许空
【2】ArrayList允许重复数据
【3】ArrayList有序,存入和取出的顺序一致,不是大小上的有序
【4】ArrayList非线程安全
【5】适合get、set元素,不需考虑线程安全的场景

(3)线程安全的解决方案
【1】方法一:重写ArrayList的方法,使用synchronized
写一个MyList继承ArrayList
【2】方法二: 使用List<Map<String,Object>> data=Collections.synchronizedList(new ArrayList<Map<String,Object>>());

【3】方法三:使用Vector

【4】方法四:使用 java.util.concurrent.CopyOnWriteArrayList;

1.1.2 Vector

(1)数据结构
底层采用数组
(2)特点
【1】Vector允许空
【2】Vector允许重复数据
【3】Vector有序
【4】Vector线程安全
【5】适合get、set元素,需考虑线程安全的场景,不过资源消耗大一些

(3)资源消耗大的解决方案
【1】使用ArrayList+线程安全解决方案

1.1.3 LinkedList

(1)数据结构
底层采用双向链表,头结点中不存放数据

(2)特点
【1】LinkedList允许空
【2】LinkedList允许重复数据
【3】LinkedList有序
【4】LinkedList 线程不安全,如果要求线程安全,可以使用ConcurrentLinkedQueue
【5】适合add、remove元素,不需考虑线程安全的场景

(3)线程安全问题的解决方案
【1】重写LinkedList的方法,使用synchronized
【2】方法一:List list = Collections.synchronizedList(new LinkedList());
【3】方法二:将LinkedList全部换成ConcurrentLinkedQueue

1.1.4 Stack

是Vector的一个子类,它实现了一个标准的后进先出的栈
(1)数据结构
底层采用数组
(2)特点
【1】Stack允许空
【2】Stack允许重复数据
【3】Stack有序
【4】Stack线程安全
【5】适合get、set元素,需考虑线程安全的场景,不过资源消耗大一些
【6】先进后出

1.2 Set

1.2.1 HashSet

HashSet是基于HashMap来实现的,操作很简单,更像是对HashMap做了一次“封装”,而且只使用了HashMap的key来实现各种特性,key就是具体的元素,value都是相等的
(1)数据结构
哈希表,本站还是数组,只不过,元素存放的位置是根据key,然后使用散列函数,计算存放位置

(2)特点
【1】HashSet 允许有 null 值
【2】HashSet 是无序的,即不会记录插入的顺序
【3】HashSet元素不可重复
hashCode和equals,当存储元素时,首先判断要存入的元素和已存在的元素的哈希值是否相同,若不相同存入,若相同则利用equals判断两个元素是否相同,若不相同,则存入,若相同则放弃。而hashCode和equlas是在存入元素自动调用的
【4】HashSet 不是线程安全的

(3)线程安全问题解决方案
【1】方法一:Set s=Collections.synchronizedSet(new Hashset<…>());
【2】方法二:用HashTable
【3】方法三:使用CopyOnWriteArraySet

1.2.2 LinkedHashSet

LinkedHashSet是具有可预知迭代顺序的Set接口的哈希表和链接列表实现。此实现与HashSet的不同之处在于,后者维护着一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序,该迭代顺序可为插入顺序或是访问顺序,前者是指按照插入时的顺序排序,后者是指按照最旧使用到最近使用的顺序

(1)数据结构
哈希表+链表

(2)特点
【1】LinkedHashSet允许有 null 值
【2】LinkedHashSet有无序的,链表会保存顺序
【3】LinkedHashSet元素不可重复
【4】LinkedHashSet不是线程安全的

(3)线程安全问题的解决方案

【1】方法一:Set s = Collections.synchronizedSet(new LinkedHashSet(…));

1.2.3 TreeSet

采用红黑树,TreeSet是用来排序的, 可以指定一个顺序, 对象存入之后会按照指定的顺序排列

(1)数据结构
红黑树,二叉平衡树

(2)特点
【1】TreeSet不允许有 null 值
【2】TreeSet 有序,是大小上的排序,元素对象要实现比较器或者定制比较器
【3】TreeSet元素不可重复
【4】TreeSet不是线程安全的

(3)线程安全问题解决方案
【1】方法一
Set s = Collections.synchronizedSet(new TreeSet(…));

1.3 Queue

1.3.1 ArrayDeQue

(1)数据结构
循环数组,相比普通数组,队头出队后的空间能循环利用
(2)特点
【1】不允许空
【2】有序
【3】可重复
【4】线程不安全
【5】当用作栈时,性能优于Stack,当用于队列时,性能优于LinkedList
【6】无容量大小限制,容量按需增长
【7】两端都可以操作, 支持同时从两端添加或移除元素,就是说可以在队头入队和出队,队尾也可以出队和入队,因此又被成为双端队列,因此可以当作队列或者栈使用

1.3.2 PriorityQueue

优先队列。优先队列的作用是能保证每次取出的元素都是队列中权值最小的
(1)数据结构
通过堆实现,具体说是通过完全二叉树(complete binary tree)实现的小顶堆(任意一个非叶子节点的权值,都不大于其左右子节点的权值),也就意味着可以通过数组来作为PriorityQueue的底层实现

(2)特点
【1】不允许空
【2】有序
【3】可重复
【4】不是线程安全

1.4 Map

1.4.1 HashMap

HashMap内部是基于哈希表实现的键值对存储,如果两个不同的元素,通过哈希函数得出的实际存储地址相同,如果key也相同,就覆盖旧值,否则在某个数组单元使用链表连接起来

注意:
在 Java 1.8 中,如果链表的长度超过了 8 ,那么链表将转化为红黑树;
发生 hash 碰撞时,Java 1.7 会在链表头部插入,而 Java 1.8 会在链表尾部插入;

(1)数据结构
哈希表+单链表,哈希表本质就是数组
(2)特点
【1】HashMap key,value都可以为null
【2】HashMap没有顺序的
【3】HashMap其键是不能重复的,它的值是可以有重复的
【4】HashMap不是线程安全的

(3)线程安全的解决方案
【1】直接使用Hashtable,但是当一个线程访问HashTable的同步方法时,其他线程如果也要访问同步方法,会被阻塞住。举个例子,当一个线程使用put方法时,另一个线程不但不可以使用put方法,连get方法都不可以,效率很低,现在基本不会选择它了。
【2】HashMap可以通过下面的语句进行同步:
Collections.synchronizeMap(hashMap);
【3】直接使用JDK 5 之后的 ConcurrentHashMap,如果使用Java 5或以上的话,请使用ConcurrentHashMap。

1.4.2 WeakHashMap

实现原理基本和HashMap一样,只是entry可能会被GC自动删除

(1)数据结构
哈希表+单链表,哈希表本质就是数组

(2)特点
【1】WeakHashMap key,value都可以为null

【2】WeakHashMap没有顺序的

【3】WeakHashMap其键是不能重复的,它的值是可以有重复的

【4】WeakHashMap不是线程安全的

【5】其键为弱键,除了自身有对key的引用外,此key没有其他引用那么此map会自动丢弃此值

1.4.3 HashTable

HashTable内部是基于哈希表实现的键值对存储,如果两个不同的元素,通过哈希函数得出的实际存储地址相同,如果key也相同,就覆盖旧值,否则在某个数组单元使用链表连接起来
(1)数据结构
哈希表+单链表,哈希表本质就是数组

(2)特点
【1】HashTable key,value都可以为null

【2】HashTable没有顺序的

【3】HashTable其键是不能重复的,它的值是可以有重复的

【4】HashTable线程安全的

(3)资源消耗大的解决方案
【1】使用HashMap+线程安全解决方案

1.4.4 TreeMap

TreeMap存储K-V键值对,通过红黑树(R-B tree)实现, 元素默认按照keys的自然排序排列

(1)数据结构
红黑树

(2)特点
【1】TreeMap key不能为null,value可以为null

【2】TreeMap有序的,大小上是排序的,可以定义比较器或者实现比较器接口

【3】TreeMap若Key重复,则后面插入的直接覆盖原来的Value

【4】TreeMap线程不安全的

(3)线程安全解决方案
【1】方法一
Collections.synchronizeMap(treeMap);

1.4.5 LinkHashMap

继承HashMap,LinkedHashMap可以认为是HashMap+LinkedList,即它既使用HashMap操作数据结构,又使用LinkedList维护插入元素的先后顺序

(1)数据结构

HashMap+LinkedList
也就是:数组+单链表+双向链表

(2)特点
【1】LinkHashMap key,value都可以为null
【2】LinkHashMap没有顺序的
【3】LinkHashMap其键是不能重复的,它的值是可以有重复的
【4】LinkHashMap不是线程安全的

(3)线程安全的解决方案
【1】方法一:Map s = Collections.synchronizedMap(new LinkedHashMap(…));

1.4.6 IdentityHashMap

继承与HashMap,但是完全不同,IdentityHashMap只有当key为同一个引用时才认为是相同的,而HashMap还包括equals相等,即内容相同。IdentityHashMap的数据很简单,底层实际就是一个Object数组,在逻辑上需要看成是一个环形的数组,解决冲突的办法是:根据计算得到散列位置,如果发现该位置上已经有元素,则往后查找,直到找到空位置,进行存放,如果没有,直接进行存放。当元素个数达到一定阈值时,Object数组会自动进行扩容处理

(1)数据结构
哈希表,也就是数组

(2)特点
【1】IdentityHashMap key,value都可以为null
【2】IdentityHashMap没有顺序的
【3】IdentityHashMap其键能重复的,但是不能同一个引用,它的值是可以有重复的
【4】IdentityHashMap不是线程安全的

【5】不同于以前的HashMap,假如底层数组是table,那么元素存放方式是:table[i]=key,table[i+1]=value

(3)线程安全的解决方案
【1】IdentityHashMap可以通过下面的语句进行同步:
Collections.synchronizeMap(identityHashMap);

2 java.util.concurrent下面的集合

结构图如下,黄线是继承,绿线是实现,还有就是有标记生成,意思是某种集合通过某个方法可以取得另外一种集合

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

2.1 List

2.1.1 CopyOnWriteArrayList

Vector对读写都是通过synchronized关键字来同步的,性能并不好。CopyOnWriteArrayList对于写操作,会使用ReentrantLock加锁,对底层数组拷贝一份,进行修改,修改完后再覆盖原来的数组,读的时候不加锁不拷贝,读取的还是原来的数组

(1)数据结构
数组

(2)特点

【1】允许空
【2】允许重复数据
【3】有序
【4】线程安全
主要通过ReentrantLock这种锁,该锁就是利用CAS+AQS队列,机制:
CAS:Compare and Swap,比较并交换。CAS有3个操作数:内存值V、预期值A、要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做;
ReentrantLock的基本实现可以概括为:先通过CAS尝试获取锁。如果此时已经有线程占据了锁,那就加入AQS队列并且被挂起。当锁被释放之后,排在CLH队列队首的线程会被唤醒,然后CAS再次尝试获取锁

【5】适合多线程,读多写少的场景,因为写的时候会拷贝数组,占用内存
【6】数组使用volatile修饰,所以写的线程修改完,读的写成可以get到最新的数据

(3)占用内存大的解决方案
Collections.synchronziedList

2.2 Set

2.2.1 CopyOnWriteArraySet

它是线程安全的无序的集合,可以将它理解成线程安全的HashSet。CopyOnWriteArraySet和HashSet虽然都继承于共同的父类AbstractSet;但是,HashSet是通过“散列表(HashMap)”实现的,而CopyOnWriteArraySet则是通过“动态数组(CopyOnWriteArrayList)”实现的,并不是散列表。和CopyOnWriteArrayList类似,其实CopyOnWriteSet底层包含一个CopyOnWriteList,几乎所有操作都是借助CopyOnWriteList,就像HashSet包含HashMap

(1)数据结构
数组,但不是散列表那种

(2)特点
【1】允许空
【2】不允许重复数据
【3】有序,因为通过单纯的数组,不通过哈希表
【4】线程安全
【5】适合多线程,读多写少的场景,因为写的时候会拷贝数组,占用内存
【6】数组使用volatile修饰,所以写的线程修改完,读的写成可以get到最新的数据

2.2.2 ConcurrentSkipListSet

ConcurrentSkipListSet是线程安全的有序的集合,适用于高并发的场景。ConcurrentSkipListSet和TreeSet,它们虽然都是有序的集合,这里的有序是大小上的排序,而不是存放和取出的顺序。但是,第一,它们的线程安全机制不同,TreeSet是非线程安全的,而ConcurrentSkipListSet是线程安全的。第二,ConcurrentSkipListSet是通过ConcurrentSkipListMap实现的,而TreeSet是通过TreeMap实现的,元素存的都是Map的key。

(1)数据结构
底层使用ConcurrentSkipListMap,是跳跃表,本质是多层链表,如下(黄色是index节点,蓝色是node节点,绿色是headIndex节点),跳表分为许多层(level),每一层都可以看作是数据的索引,这些索引的意义就是加快跳表查找数据速度。每一层的数据都是有序的,上一层数据是下一层数据的子集,并且第一层(level 1)包含了全部的数据;层次越高,跳跃性越大,包含的数据越少。跳表包含一个表头,它查找数据时,是从上往下,从左往右进行查找,比如查找32的路线:head-21-32(绿色)

在这里插入图片描述

(2)特点
【1】key和value都不允许空,当然这里set集合我们只关注key
【2】不允许重复数据
【3】有序,大小上升序的
【4】线程安全
用到了cas锁,机制:Compare and Swap即比较并交换。CAS有三个操作数:内存值V、旧的预期值A、要修改的值B,原值是A,内存的值是V,在内存值V=A,也就是没有给修改的情况下,当前线程可将其修改为B,然后返回true,否则什么也不做,然后返回false
【5】在非多线程的情况下,应当尽量使用TreeMap。此外对于并发性相对较低的并行程序可以使用Collections.synchronizedSortedMap将TreeMap进行包装,也可以提供较好的效率。对于高并发程序,应当使用ConcurrentSkipListMap,能够提供更高的并发度

【6】层数是根据一种随机算法得到的,为了不让层数过大,还会有一个最大层数MAX_LEVEL限制,随机算法生成的层数不得大于该值。关于层数是如何来增加的,这个就依靠于ThreadLocalRandom.nextSecondarySeed()这个随机数来决定,当该随机数的二进制最高位与最末位不为0的时候,我们put进该Map的数据只会在最底层链表中,不会在高层链表中构建节点。当该随机数的二进制最高位与最末位都为0的时候,且该随机数从二进制第二位开始向左有多少个1,就代表会在多少层高层链表中构建节点,当然超过原跳表的最高层只会增加一层

2.3 Queue

2.3.1 ConcurrentLinkedQueue

ConcurrentLinkedQueue是一种非阻塞的线程安全队列,与阻塞队列LinkedBlockingQueue相对应,ConcurrentLinkedQueue同样也是使用链表实现的FIFO队列,但不同的是它没有使用任何锁机制,而是用CAS来实现线程安全。
(1)数据结构
链表
(2)特点
【1】不允许添加null
【2】可重复
【3】单端
【4】非阻塞
【5】线程安全
使用CAS机制(乐观锁)来实现线程安全

2.3.2 ConcurrentLinkedDeQue

CocurrenLinkedDeQueue是一种非阻塞的线程安全的双端队列,与阻塞双端队列LinkedBlockingDeQueue相对应,ConcurrentLinkedDeQueue同样也是使用链表实现的FIFO队列,但不同的是它没有使用任何锁机制,而是用CAS来实现线程安全。
(1)数据结构
双向链表
(2)特点
【1】不允许添加null
【2】可重复
【3】双端
【4】非阻塞
【5】线程安全
使用CAS机制(乐观锁)来实现线程安全

2.3.3 ArrayBlockingQueue

ArrayBlockingQueue是一个阻塞的线程安全的队列,底层采用数组实现
(1)数据结构
数组
(2)特点
【1】不允许添加null
【2】可重复
【3】单端
【4】阻塞
【5】线程安全
采用一把ReentrantLock锁(悲观锁)来保证线程安全

2.3.4 DelayQueue

DelayQueue是一个支持延时获取元素的无界阻塞队列。里面的元素全部都是“可延期”的元素,列头的元素是最先“到期”的元素,如果队列里面没有元素到期,是不能从列头获取元素的,哪怕有元素也不行。也就是说只有在延迟期到时才能够从队列中取元素。DelayQueue主要用于两个方面:清掉缓存中超时的缓存数据和任务超时处理
(1)数据结构
链表
(2)特点
【1】不允许添加null
【2】可重复
【3】有序,按照优先级排序
每个元素都有个过期时间,并且队列是个优先级队列,当从队列获取元素时候,只有过期元素才会出队列,最先过期的元素放在优先级最高,根据元素的过期时间来对元素进行排列,因此,先过期的元素会在队首

【3】单端
【4】阻塞
无界,入队不会阻塞;出队为空的时候才会阻塞
【5】线程安全
采用一把ReentrantLock锁(悲观锁)来保证线程安全

2.3.5 PriorityBlockingQueue

是一个支持优先级的无界阻塞队列,直到系统资源耗尽。默认情况下元素采用自然顺序升序排列。也可以自定义类实现compareTo()方法来指定元素排序规则,或者初始化PriorityBlockingQueue时,指定构造参数Comparator来对元素进行排序。但需要注意的是不能保证同优先级元素的顺序
。PriorityBlockingQueue也是基于最小二叉堆实现,使用基于CAS实现的自旋锁来控制队列的动态扩容,保证了扩容操作不会阻塞take操作的执行。
(1)数据结构
数组
(2)特点
【1】不允许null
【2】有序
【3】可重复
【4】单端
【5】阻塞
【6】线程安全
使用CAS机制(乐观锁)来实现线程安全

2.3.6 LinkedBlockingQueue

LinkedBlockingQueue是一个阻塞的线程安全的队列,底层采用链表实现
(1)数据结构
链表
(2)特点
【1】不允许添加null
【2】可重复
【3】单端
【4】阻塞
无界,入队不阻塞,出队队空的话会阻塞
【5】线程安全
采用一把ReentrantLock锁(悲观锁)来保证线程安全

2.3.7 LinkedBlockingDeQueue

LinkedBlockingDeQueue是一个阻塞的线程安全的双端队列,底层采用链表实现

(1)数据结构
双向链表

(2)特点
【1】不允许添加null
【2】可重复
【3】双端
双端队列是一个你可以从任意一端插入或者抽取元素的队列。实现了在队列头和队列尾的高效插入和移除
【4】阻塞
【5】线程安全
采用一个可重入锁ReentrantLock锁(悲观锁)来保证线程安全

2.3.8 synchronousQueue

SynchronousQueue作为阻塞队列的时候,对于每一个take的线程会阻塞直到有一个put的线程放入元素为止,反之亦然。在SynchronousQueue内部没有任何存放元素的能力。所以类似peek操作或者迭代器操作也是无效的,元素只能通过put类操作或者take类操作才有效。通常队列的第一个元素是当前第一个等待的线程。如果没有线程阻塞在该队列则poll会返回null。从Collection的视角来看SynchronousQueue表现为一个空的集合。
SynchronousQueue是java里的无缓冲队列,用于在两个线程之间直接移交元素;有两种实现方式,一种是公平(队列)方式,一种是非公平(栈)方式;所以底层可能两种数据结构:队列(实现公平策略)和栈(实现非公平策略),队列与栈都是通过链表来实现的。

(1)数据结构
链表

(2)特点
【1】不允许添加null
【2】可重复
【3】单端
【4】阻塞
阻塞是代价非常大的操作,要保存当前线程的很多数据,并且要切换上下文,所以通常来说在阻塞之前都先自旋,自旋其实就是在一个循环里不停的检测是否有效,当然这要设定时间限。如果在时间限内通过自旋完成了操作。那就不需要去阻塞这也自然是最好的提高了响应速度。但是如果自旋时间限内还是能没能完成操作那就只有阻塞了,但是自旋时间过长会浪费cpu时间
【5】线程安全
采用cas机制来保证线程安全

2.3.9 LinkedTransferQueue

无界的阻塞队列LinkedTransferQueue,此队列也是基于链表的,对于所有给定的生产者都是先入先出的,可以把它看成是SynchronousQueue和LinkedBlockingQueue的超集
(1)数据结构
链表

(2)特点
【1】不允许添加null
【2】可重复
【3】单端
【4】阻塞
【5】线程安全
采用cas机制来保证线程安全

2.4 Map

2.4.1 ConcurrentSkipListMap

底层使用ConcurrentSkipListMap,是跳跃表,本质是多层链表,如下(黄色是index节点,蓝色是node节点,绿色是headIndex节点),跳表分为许多层(level),每一层都可以看作是数据的索引,这些索引的意义就是加快跳表查找数据速度。每一层的数据都是有序的,上一层数据是下一层数据的子集,并且第一层(level 1)包含了全部的数据;层次越高,跳跃性越大,包含的数据越少。跳表包含一个表头,它查找数据时,是从上往下,从左往右进行查找,比如查找32的路线:head-21-32(绿色)
在这里插入图片描述
(1)数据结构
多层单链表

(2)特点
【1】key和value都不允许空
【2】不允许重复数据
【3】有序,大小上升序的
【4】线程安全
用到了cas锁,机制:Compare and Swap即比较并交换。CAS有三个操作数:内存值V、旧的预期值A、要修改的值B,原值是A,内存的值是V,在内存值V=A,也就是没有给修改的情况下,当前线程可将其修改为B,然后返回true,否则什么也不做,然后返回false
【5】在非多线程的情况下,应当尽量使用TreeMap。此外对于并发性相对较低的并行程序可以使用Collections.synchronizedSortedMap将TreeMap进行包装,也可以提供较好的效率。对于高并发程序,应当使用ConcurrentSkipListMap,能够提供更高的并发度

【6】层数是根据一种随机算法得到的,为了不让层数过大,还会有一个最大层数MAX_LEVEL限制,随机算法生成的层数不得大于该值。关于层数是如何来增加的,这个就依靠于ThreadLocalRandom.nextSecondarySeed()这个随机数来决定,当该随机数的二进制最高位与最末位不为0的时候,我们put进该Map的数据只会在最底层链表中,不会在高层链表中构建节点。当该随机数的二进制最高位与最末位都为0的时候,且该随机数从二进制第二位开始向左有多少个1,就代表会在多少层高层链表中构建节点,当然超过原跳表的最高层只会增加一层

2.4.2 ConcurrentHashMap

(1)数据结构
【1】在JDK1.7之前,ConcurrentHashMap的数据结构是由一个Segment数组和多个HashEntry组成;Segment数组的意义就是将一个大的table分割成多个小的table来进行加锁,也就是上面的提到的锁分离技术,而每一个Segment元素存储的是HashEntry数组+链表,这个和HashMap的数据存储结构一样。

Segment数组的意义就是将一个大的table分割成多个小的table来进行加锁,也就是上面的提到的锁分离技术,而每一个Segment元素存储的是HashEntry数组+链表,这个和HashMap的数据存储结构一样

在这里插入图片描述
【2】jdk1.8之后
摒弃了Segment的概念,而是直接用Node数组+链表+红黑树的数据结构来实现,并发控制使用Synchronized和CAS来操作,整个看起来就像是优化过且线程安全的HashMap,虽然在JDK1.8中还能看到Segment的数据结构,但是已经简化了属性,只是为了兼容旧版本。Node是ConcurrentHashMap存储结构的基本单元,继承于HashMap中的Entry,用于存储数据,当链表的节点数大于8时会转换成红黑树的结构,这时候会用TreeNode

在这里插入图片描述

(2)特点
【1】key和value都不允许空
【2】key不允许重复数据,value可以重复
【3】无序
【4】线程安全
Jdk1.7之前,Segment实现了ReentrantLock,也就带有锁的功能,当执行put操作时,会进行第一次key的hash来定位Segment的位置,如果该Segment还没有初始化,即通过CAS操作进行赋值,然后进行第二次hash操作,找到相应的HashEntry的位置,这里会利用继承过来的锁的特性,在将数据插入指定的HashEntry位置时(链表的尾端),会通过继承ReentrantLock的tryLock()方法尝试去获取锁,如果获取成功就直接插入相应的位置,如果已经有线程获取该Segment的锁,那当前线程会以自旋的方式去继续的调用tryLock()方法去获取锁,超过指定次数就挂起,等待唤醒;

Jdk18之后,使用cas锁操作node节点,如果没有hash冲突就直接CAS插入,如果存在hash冲突,就加锁来保证线程安全(乐观锁),这里有两种情况,一种是链表形式就直接遍历到尾端插入,一种是红黑树就按照红黑树结构插入

总结:JDK1.7版本:ReentrantLock+Segment+HashEntry, JDK1.8版本:synchronized+CAS+HashEntry+红黑树

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Java封神之路

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值