成神之路, 需要耐得住寂寞, 开启总结源码之旅.
我阅读总结源码的目的不是为了炫技, 我希望通过阅读源码可以解决一些问题, 也可以通过阅读源码理解别人思想, 以帮助我们更好的写我们的代码.
引子
在多线程的场景中, 我们需要如何同步数据, 通常会使用 synchronized 或者 lock 来处理, 使用了 synchronized 意味着内核态的一次切换. 这是一个很重的操作. 有没有一种方式, 可以比较便利的实现一些简单的数据同步, 比如计数器等等. concurrent 包下的 atomic 提供我们这么一种轻量级的数据同步的选择.
他山之石
说一说 Java 的 Unsafe 类: https://www.cnblogs.com/pkufork/p/java_unsafe.html
java 魔法之 unsafe:
java 乐观锁实现案例:
Java 并发问题 -- 乐观锁与悲观锁以及乐观锁的一种实现方式 - CAS: https://www.cnblogs.com/qjjazry/p/6581568.html
JDK8 系列之 LongAdder 解析: https://www.jianshu.com/p/ec045c38ef0c
jdk1.8 LongAdder 源码学习:
使用例子
- import java.util.concurrent.CountDownLatch;
- import java.util.concurrent.atomic.AtomicInteger;
- public class App { public static void main(String[] args) throws Exception {
- CountDownLatch countDownLatch = new CountDownLatch(100);
- AtomicInteger atomicInteger = new AtomicInteger(0);
- for (int i = 0; i < 100; i++) {
- new Thread() {
- @Override
- public void run() {
- atomicInteger.getAndIncrement();
- countDownLatch.countDown();
- }
- }.start();
- }
- countDownLatch.await();
- System.out.println(atomicInteger.get());
- }
- }
复制代码
请暂时先忽略 CountDownLatch, 只是为了在主线程中等待所有子线程执行完, 打印结果. 这个结果永远都是 100. 如果将 AtomicInteger 换成 Integer, 打印结果基本都是小于 100.
原理
我们可以看一下 AtomicInteger 的代码
他的值是存在一个 volatile 的 int 里面. volatile 只能保证这个变量的可见性. 不能保证他的原子性.
可以看看 getAndIncrement 这个类似 i++ 的函数, 可以发现, 是调用了 UnSafe 中的 getAndAddInt.
UnSafe 是何方神圣? 可以参考上面的文章了解一下, UnSafe 提供了 java 可以直接操作底层的能力. 进一步, 我们可以发现实现方式:
如何保证原子性: 自旋 + CAS(乐观锁). 在这个过程中, 通过 compareAndSwapInt 比较更新 value 值, 如果更新失败, 重新获取旧值, 然后更新.
优缺点
CAS 相对于其他锁, 不会进行内核态操作, 有着一些性能的提升. 但同时引入自旋, 当锁竞争较大的时候, 自旋次数会增多. cpu 资源会消耗很高.
换句话说, CAS + 自旋适合使用在低并发有同步数据的应用场景.
jdk8 做出的改进和努力
在 jdk8 中引入了 4 个新的计数器类型, LongAdder,LongAccumulator,DoubleAdder,DoubleAccumulator. 他们都是继承于 Striped64.
在 LongAdder 与 AtomicLong 有什么区别? Atomic * 遇到的问题是, 只能运用于低并发场景. 因此 LongAddr 在这基础上引入了分段锁的概念. 可以参考《JDK8 系列之 LongAdder 解析》一起看看做了什么.
大概就是当竞争不激烈的时候, 所有线程都是通过 CAS 对同一个变量 (Base) 进行修改, 当竞争激烈的时候, 会将根据当前线程哈希到对于 Cell 上进行修改(多段锁).
可以看到大概实现原理是: 通过 CAS 乐观锁保证原子性, 通过自旋保证当次修改的最终修改成功, 通过 ** 降低锁粒度(多段锁)** 增加并发性能.
关键点
自旋
CAS 乐观锁
多段锁(分治思想)
来源: https://juejin.im/post/5b9e5279f265da0af7750c3f