Java并发系列「3」---- 容器

@TOC# Java并发系列
记录在程序走的每一步___auth:huf


经常看我的文章的同学 知道我是比较少讲基础篇章的;

该篇章作为并发容器的介入;
作者希望能够由浅到深 进行一个讲解;
本篇文章不会讲解到所有容器;
仅仅会讲解到涉及到知识点的容器;
本文主要讲的是一个面试重点 Map 以及 ConcurrentHashMap的一个知识体系的原理;
如果涉及到知识点本章节没有讲解清楚的 请不要着急; 后面篇章会补齐;


我们先谈一谈容器。 Java的容器 是基础篇里面的最重要的一章;
我们在业务中 大量的使用到各种容器;
我个人把容器 分为这么几个层次:


从多线程的线程安全角度去看:

  1. 并发容器 : 线程安全的 - 效率低
  2. 非并发容器 :非线程安全的 - 效率高

从容器结构去看:

  1. Queue : 队列
  2. Map: key value 键值对
  3. List (Set):集合 List 是有序,可重复, Set 是无序,无可重复的。

  我们就以此背景 开启我们容器篇章;
  我们并非是讲解容器是怎么使用。
  我的想法 是将更底层的一些组成原理给讲透彻;
  由浅入深逐步往里面研究; 

List

 ArrayList  底层由数组组成;  事件复杂度查询是O(1): 插入是O(N) 
 			每次插入的时候会去检查是否需要扩容;非线程安全的;
 			如果单单调用add(Object)时间 复杂度是O(1); 
 			因为它会在数组尾部进行数据的添加;
 			优化方案:提前知晓数组长度 提前扩容数组;减少扩容次数;


LinkedList: 底层是由链表组成; 时间复杂度 查询O(N) 插入 删除是O(1)
			链表不是数组; 链表是另外一种数据结构; 节点以对象Node进行组装; 
			每个Node 都记录着上面一个Node信息 以及下面一个Node信息;
			因此查找的时候需要每个Node 循环查找下去; 这样查询时间复杂度就是O(N) 
			插删及是插拔模式。 即改变其Node的上个节点信息 以及他的下个节点信息 即可做成替换
			linkedList 是一个双向链表; 下一个节点next 上一个节点prev

ArrayList 以及LinkedList 同样使用 add方法; 其效率 主要是看ArrayList的扩容机制; 在LinkedList里面 是不存在扩容机制的。 在ArrayList里面的扩容机制。 如果一开始 就知道ArrayList 的容量体积大小; 那么ArrayList 的效率 一定是比 LinkedList 高。 这一点如果面试官面到了 需要重点回答的;


Set

set底层是使用 Map的key   我们看一下源码;

在这里插入图片描述
PRESENT 是
在这里插入图片描述
一个空对象;
所以我们重点放在Map;


Map

HashMap:  我们再开发中最常用到的一个结构。 底层 是由 数组,链表,红黑树 组成(1.8)尾插法 
		1.7是由 数组 链表 组成; 使用的是头插法 
		以下一张图带你们了解整个HashMap 以及其底层代码;

在这里插入图片描述
图内字有点小:

  1. 数组长度 :数组长度初始长度为16
  2. 数组的扩容因子是0.75,例如长度为16 ,当长度到达12的时候扩容
  3. 树化长度必须要大于64
  4. 为什么HashMap扩容会是原先的2倍,业务其容量是N的2次幂;
  5. Node 会先Hash 散列算法 算出下标;然后把相对应的Node 放到下标处
  6. 如果Node散列出相同位置;那么该位置就会形成一个链表;

以下是Map 节点 链表与红黑树之间的转换 以及转换阈值 红黑树对比链表 由什么好处?

在这里插入图片描述
链表大家都知道 如果要查询的情况 肯定是第一位开始往下查 链表 是查询慢的一种数据结构;
但是 红黑树; 不一样。
在这里插入图片描述
红黑树 我们遵从2个原则 就是 左小 右大 ; 我们从根节点开始比对;
如果比根节点下 就从左边往下找; 如果比跟节点大 就从右边往下找。
如果数据量足够大;查找效率就会比链表快接近一倍;
红黑树有一个左旋操作 因此红黑树是有个插入效率问题的;
在后面我们数据结构与算法篇章 我会提出;

总结:
	在HashMap 这个数据结构中。 在面试期间问的相当多。
	HashMap在我们业务逻辑的场景下 也使用的非常非常多;
	我们必须要掌握;
补充: 
		在JDK 1.7 Map使用的是 头插法 put的对象 在hash相同的情况下 ;
		那么就会插入到Node.pre  在多个Thread 的情况下; 就会产生 A覆盖B B覆盖A;
		进入死循环的情况 、
		
		在JDK1.8 Map使用的是尾插法 put的对象 在hash相同的情况下;
		会插入到Node.next  在多个Thread的情况下;修复了 AB问题;

ConcurrentHashMap:是线程安全的; 在变成链表头部 就会进行加锁 锁粒度是代码块级别的;
在这里插入图片描述
首先 我们发现
ConcurrentHashMap 采用了懒加载的方式 在他的创建中
不会有任何操作 知道put的数据 才会初始化整个容器;
在这里插入图片描述

ConcurrentHashMap  使用了大量的 CAS (后面篇章会讲CAS 自旋锁)
这里提一嘴  CAS自旋锁 与我们的LOCK 锁 有什么区别?  
CAS自旋锁是乐观锁;
LOCK 锁是悲观锁;

乐观锁与悲观锁 的区别 我们在之后的文章会有更详细的了解;
现在我们只需要知道他们一个是乐观锁一个是悲观锁

Hashtable:使用了synchronized 进行加锁 但是锁是方法级别的; 所以得出结论
ConcurrentHashMap  一定是比HashTable 效率更高的;

在这里插入图片描述


总结

  • 我们用ArrayList 引出了 数组
  • 我们用LinkedList 引出了 链表
  • 我们用Map 引出了 数组 链表 红黑树
  • 我们用ConcurrentHashMap 引出了CAS 乐观锁 以及悲观锁

本章节负责引出; 后面会有章节帮这里面的相关未讲解的知识点全部补充

seeyou

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Like Java Long Time

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

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

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

打赏作者

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

抵扣说明:

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

余额充值