一、Atomic 体系

1、简单归纳

image.png image.png

2、AtomicInteger

java.util.concurrent.atomic.AtomicInteger,AtomicInteger 是对 int 类型的一个封装,提供原子性的访问和更新操作,它的使用场景是并发统计和订单号生成,但是分布式下订单号生成却不能避免重复。
常见的 api 有:
java.util.concurrent.atomic.AtomicInteger#incrementAndGet 原子自增 1;
java.util.concurrent.atomic.AtomicInteger#decrementAndGet 原子自减 1;
java.util.concurrent.atomic.AtomicInteger#addAndGet 原子在当前值加上给定的值;
java.util.concurrent.atomic.AtomicInteger#get 返回当前最新的值。

3、CAS

sun.misc.Unsafe#compareAndSwapInt,
CAS 有 3 个操作数,内存值 V,旧的预期值 A,要修改的新值 B。当且仅当预期 值 A 和内存值 V 相同时,将内存值 V 修改为 B,否则什么都不做。
CAS 算法: CAS 的全称是 Compare And Swap 即比较交换,其算法核心思想如下 函数:CAS(V,E,N) 参数:V 表示要更新的变量 E 预期值 N 新值 如果 V 值等于 E 值,则将 V 的值设为 N。若 V 值和 E 值不同,则说明已经有其他线程做了 更新,则当前线程什么都不做。通俗的理解就是 CAS 操作需要我们提供一个期望值,当期 望值与当前线程的变量值相同时,说明还没线程修改该值,当前线程可以进行修改,也就是 执行 CAS 操作,但如果期望值与当前线程不符,则说明该值已被其他线程修改,此时不执 行更新操作,但可以选择重新读取该变量再尝试再次修改该变量,也可以放弃操作。

Java 的 CAS 操作通过 Unsafe 类来完成,该类里面基本都是 native,即通过 JNI 调用 c/c++等代码。底层核心:cmpxchg。cmpxchg 是汇编指令,它的作用是比较并交换操作数。如:CMPXCHG r/m,r 将累加器 AL/AX/EAX/RAX 中的值与首操作数(目的操作数)比较,如果相等,第 2 操作数(源操作数)的值装载到首操作数,置为 1。如果不等,首操作数的值装载到 AL/AX/EAX/RAX 并将置为清 0 该指令只能用于 486 及其后继机型。第 2 操作数(源操作数)只能用 8 位、16 位或 32 位寄存器。第 1 操作数(目地操作数)则可用寄存器或任一种存储器寻址方式。

CAS 原理解析:
利用 CPU 的 CAS 指令,同时借助 JNI 来完成 Java 的非阻塞算法,其它原子操作都是利用类似的特性完成的。 在 java.util.concurrent 下面的源码中,Atomic, ReentrantLock 都使用了 Unsafe 类中的方法来保证并发的安全性。 CAS 操作是原子性的,所以多线程并发使用 CAS 更新数据时,可以不使用锁,JDK 中大量使用了 CAS 来更新数据而防止加锁来保持原子更新。
CAS 操作包含三个操作数 :内存偏移量位置(V)、预期原值(A)和新值(B)。 如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值 ,否则处理器不做任何操作。

CAS的 ABA问题:
在进行 CAS操作的时候,因为在更改 V 之前,CAS主要询问“V的值是否仍然为A”,所以在第一次读取 V之后以及对 V执行 CAS操作之前,如果将值从 A改为B,然后再改回 A,会使基于 CAS 的算法混乱。在这种情况下,CAS 操作会成功。这类问题称为 ABA问题。

4、AtomicBoolean

Atomic 实现都是 cas,它的使用场景可以在某些场合下替换 volatile。

二、Collections

1、HashMap

HashMap 底层数据结构是:数组+链表+红黑树。

jdk1.7版本:数组+链表
image.png

jdk1.8版本:数组+链表+红黑树,默认使用链表,只有在链表节点超过8时开变成红黑树。
image.png

在初始化时,先使用数组,当键值对占总量一定比例时开始扩容(默认0.75),遇到哈希冲突💥的时候,会在相应下标下开始进行链表生成,当链表长度超过8时开始拆分为红黑树结构。
image.png

2、HashTable ConcurrentHashMap

和 hashmap 相比,他们都不允许 key 为 null,但是它们在put的时候通过分段锁来插入数据。

3、ArrayList CopyOnWriteArrayList

ArrayList:底层结构是数组,默认容量是10,非线程安全;
LinkedList:底层结构是双向链表;

CopyOnWriteArrayList 原理解析:
读写分离、读多写少 黑白名单
步骤:
1、如果写操作未完成,那么直接读取原数组的数据;
2、如果写操作完成,但是引用还未指向新数组,那么也是读取原数组数据;
3、如果写操作完成,并且引用已经指向了新的数组,那么直接从新数组中读取。

image.png

CopyOnWriteArrayList 的 put方法有锁,是线程安全的。