JUC
java不能直接访问操作系统底层,而是通过native本地方法来访问。Unsafe类提供了硬件级别的原子操作juc并发包,即java.util.concurrent包,是JDK的核心工具包,是JDK1.5之后,由 Doug Lea实现并引入。整个java.util.concurrent包,按照功能可以大致划分如下:
- juc-locks 锁框架
- juc-atomic 原子类框架
- juc-sync 同步器框架
- juc-collections 集合框架
- juc-executors 执行器框架
CAS:Compare And Swap比较并替换
www.jianshu.com/p/a142350e9… blog.csdn.net/v123411739/…
概念:是使用一个期望值和一个变量的当前值进行比较,如果当前变量的值与我们期望的值相等,就使用一个新值替换当前变量的值。CAS算法的过程是这样:它包含三个参数 CAS(V,E,N)。V表示要更新的变量,E表示预期的值,N表示新值。仅当V值等于E值时,才会将V的值设置成N,否则什么都不做。最后CAS返回当前V的值。CAS算法需要你额外给出一个期望值,也就是你认为现在变量应该是什么样子,如果变量不是你想象的那样,那说明已经被别人修改过。你就重新读取,再次尝试修改即可。
CAS操作被封装在Unsafe类下,由于需要直接操作内存值"value",通过unsafe获取内存值"value"的内存地址(该地址是相对于AtomicaInteger对象的内存地址,Java会将AtomicaInteger对象的内存地址 + valueOffset映射成实际内存地址)。由于是相对内存地址,所以对于任意一个AtomicaInteger对象,该地址是固定的,只需要在static初始化时获取该地址即可。
2. CAS存在的问题和解决方案:
CAS虽然很高效的解决了原子操作问题,但是CAS仍然存在三大问题:
1.ABA问题 因为CAS需要在操作值的时候检查下值有没有发生变化,如果没有发生变化则更新,但是如果一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时会发现它的值没有发生变化,但是实际上却变化了。ABA问题的解决思路就是使用版本号。在变量前面追加上版本号,每次变量更新的时候把版本号加一,那么A-B-A 就会变成1A-2B-3A
2.循环时间开销大:自旋CAS如果长时间不成功,会给CPU带来非常大的执行开销(时刻a的内存值和时刻b的内存值比较,如果相等则更新,否则一直循环) 解决方案:
- AtomicMarkableReference 维护了boolean变量表示引用变量是否被更改过
- AtomicStampedReference 维护了int类型的变量表示版本号
3.乐 观 锁 只 能 保 证 一 个 共 享 变 量 的 原 子 操 作 。 如 果 多 一 个 或 几 个 变 量 , 乐观 锁 将 变 得 力 不 从 心 , 但 互 斥 锁 能 轻 易 解 决 , 不 管 对 象 数 量 多 少 及 对 象颗 粒 度 大 小