From 224f1e07c131f85e61c6257dbdfbed095e26e0d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=B6=E5=AD=90?= Date: Wed, 18 Dec 2019 13:57:30 +0800 Subject: [PATCH 1/7] unsafe-009 --- week_02/09/unsafe-009.md | 161 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 week_02/09/unsafe-009.md diff --git a/week_02/09/unsafe-009.md b/week_02/09/unsafe-009.md new file mode 100644 index 0000000..cf6ef8f --- /dev/null +++ b/week_02/09/unsafe-009.md @@ -0,0 +1,161 @@ +###关于Unsafe + +####1、认识unsafe + +1)全名sun.misc. unsafe + +2)final定义,不能被继承,成员方法没有机会被覆盖,默认都是final的 + +3)构造函数为private,即不允许被外部实例化 + +```java + +public final class Unsafe { + private static final Unsafe theUnsafe; + public static final int INVALID_FIELD_OFFSET = -1; + public static final int ARRAY_BOOLEAN_BASE_OFFSET; + public static final int ARRAY_BYTE_BASE_OFFSET; + public static final int ARRAY_SHORT_BASE_OFFSET; + public static final int ARRAY_CHAR_BASE_OFFSET; + public static final int ARRAY_INT_BASE_OFFSET; + public static final int ARRAY_LONG_BASE_OFFSET; + public static final int ARRAY_FLOAT_BASE_OFFSET; + public static final int ARRAY_DOUBLE_BASE_OFFSET; + public static final int ARRAY_OBJECT_BASE_OFFSET; + public static final int ARRAY_BOOLEAN_INDEX_SCALE; + public static final int ARRAY_BYTE_INDEX_SCALE; + public static final int ARRAY_SHORT_INDEX_SCALE; + public static final int ARRAY_CHAR_INDEX_SCALE; + public static final int ARRAY_INT_INDEX_SCALE; + public static final int ARRAY_LONG_INDEX_SCALE; + public static final int ARRAY_FLOAT_INDEX_SCALE; + public static final int ARRAY_DOUBLE_INDEX_SCALE; + public static final int ARRAY_OBJECT_INDEX_SCALE; + public static final int ADDRESS_SIZE; + + private static native void registerNatives(); + + private Unsafe() { + } + .... + +} + +``` + +####2、获取unsafe + +```java + +@CallerSensitive +public static Unsafe getUnsafe() { + //通过反射获取class + Class var0 = Reflection.getCallerClass(); + if (!VM.isSystemDomainLoader(var0.getClassLoader())) { + throw new SecurityException("Unsafe"); + } else { + return theUnsafe; + } +} + +//test 获取unsafe 实例 +public static void main(String[] args) { + try { + //获取unsafe 实例 + Field f = Unsafe.class.getDeclaredField("theUnsafe"); + f.setAccessible(true); + Unsafe unsafeTest = (Unsafe) f.get(null); + System.out.println(unsafeTest); //sun.misc.Unsafe@a74868d + + } catch (NoSuchFieldException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + +``` + +####3、Volatile读写 + +```java + +public native int getIntVolatile(Object var1, long var2); + +public native void putIntVolatile(Object var1, long var2, int var4); + +``` + + +####4、直接操作内存 + +```java + +//分配内存 +public native long allocateMemory(long var1); +//重新分配内存 +public native long reallocateMemory(long var1, long var3); +//内存初始化 +public native void setMemory(Object var1, long var2, long var4, byte var6); + +public void setMemory(long var1, long var3, byte var5) { + this.setMemory((Object)null, var1, var3, var5); +} +//内存复制 +public native void copyMemory(Object var1, long var2, Object var4, long var5, long var7); + +public void copyMemory(long var1, long var3, long var5) { + this.copyMemory((Object)null, var1, (Object)null, var3, var5); +} +//清除内存 +public native void freeMemory(long var1); + + +``` + +####5、CAS相关 + +Unsafe中提供了int,long和Object的CAS操作 + +```java + +public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5); + +public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5); + +public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6); + +``` + +####6、线程调度 + +```java +//挂起线程 +public native void unpark(Object var1); +//唤醒线程 +public native void park(boolean var1, long var2); +//用于加锁 +public native void monitorEnter(Object var1); +//用于加锁 +public native void monitorExit(Object var1); + +public native boolean tryMonitorEnter(Object var1); + +``` + +####7、类加载 + +```java +//用于动态地创建类 +public native Class defineClass(String var1, byte[] var2, int var3, int var4, ClassLoader var5, ProtectionDomain var6); +//用于动态的创建一个匿名内部类 +public native Class defineAnonymousClass(Class var1, byte[] var2, Object[] var3); +//用于创建一个类的实例,但是不会调用这个实例的构造方法,如果这个类还未被初始化,则初始化这个类。 +public native Object allocateInstance(Class var1) throws InstantiationException; +//用于判断是否需要初始化一个类。 +public native boolean shouldBeInitialized(Class var1); +//用于判断是否需要初始化一个类。 +public native void ensureClassInitialized(Class var1); + +``` + + -- Gitee From 12a0cb0dfe58460a0d3987023401cb38fe85aeba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=B6=E5=AD=90?= Date: Wed, 18 Dec 2019 14:03:50 +0800 Subject: [PATCH 2/7] update 009 unsafe --- week_02/09/unsafe-009.md | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/week_02/09/unsafe-009.md b/week_02/09/unsafe-009.md index cf6ef8f..7c598c2 100644 --- a/week_02/09/unsafe-009.md +++ b/week_02/09/unsafe-009.md @@ -1,12 +1,13 @@ -###关于Unsafe +关于Unsafe -####1、认识unsafe +1、认识unsafe -1)全名sun.misc. unsafe + 1)全名sun.misc. unsafe -2)final定义,不能被继承,成员方法没有机会被覆盖,默认都是final的 + 2)final定义,不能被继承,成员方法没有机会被覆盖,默认都是final的 + + 3)构造函数为private,即不允许被外部实例化(如下截图) -3)构造函数为private,即不允许被外部实例化 ```java @@ -43,7 +44,7 @@ public final class Unsafe { ``` -####2、获取unsafe +2、获取unsafe ```java @@ -75,7 +76,7 @@ public static void main(String[] args) { ``` -####3、Volatile读写 +3、Volatile读写 ```java @@ -86,7 +87,7 @@ public native void putIntVolatile(Object var1, long var2, int var4); ``` -####4、直接操作内存 +4、直接操作内存 ```java @@ -112,7 +113,7 @@ public native void freeMemory(long var1); ``` -####5、CAS相关 +5、CAS相关 Unsafe中提供了int,long和Object的CAS操作 @@ -126,7 +127,7 @@ public final native boolean compareAndSwapLong(Object var1, long var2, long var4 ``` -####6、线程调度 +6、线程调度 ```java //挂起线程 @@ -142,7 +143,7 @@ public native boolean tryMonitorEnter(Object var1); ``` -####7、类加载 +7、类加载 ```java //用于动态地创建类 -- Gitee From 7994a22852e7da7e9b292fdd9e8ee369bf26d0ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=B6=E5=AD=90?= Date: Thu, 19 Dec 2019 09:34:20 +0800 Subject: [PATCH 3/7] AtomicInteger-009 --- week_02/09/AtomicInteger-009.md | 59 +++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 week_02/09/AtomicInteger-009.md diff --git a/week_02/09/AtomicInteger-009.md b/week_02/09/AtomicInteger-009.md new file mode 100644 index 0000000..6f86fba --- /dev/null +++ b/week_02/09/AtomicInteger-009.md @@ -0,0 +1,59 @@ +###关于AtomicInteger + +####1、什么是原子性?原子操作? + + java特性之一,原子性指一个操作是不可中断的。即使是多个线程一起执行的时候,一个操作一旦开始,就不会被其他线程干扰; + + 原子操作可以是一个步骤,也可以是多个操作步骤,但是其顺序不可以被打乱,也不可以被切割而只执行其中的一部分,将整个操作视作一个整体是原子性的核心特征 + +####2、认识AtomicInteger +1)继承number类,实现序列化接口 + +```java +public class AtomicInteger extends Number implements java.io.Serializable + +``` +2)属性 + +```java +//获取Unsafe的实例 +private static final Unsafe unsafe = Unsafe.getUnsafe(); +//标识value字段的偏移量 +private static final long valueOffset; +//存储int类型值的地方,使用volatile修饰(采用 "内存屏障" 来实现) +private volatile int value; +//静态代码块,通过unsafe获取value的偏移量 +static { + try { + valueOffset = unsafe.objectFieldOffset + (AtomicInteger.class.getDeclaredField("value")); + } catch (Exception ex) { throw new Error(ex); } +} +``` + +ps:使用int类型的value存储值,且使用volatile修饰,volatile主要是保证可见性,即一个线程修改对另一个线程立即可见。 + +3)compareAndSet(int expect, int update) 方法 + +```java + +public final boolean compareAndSet(int expect, int update) { + //调用Unsafe CAS操作 + return unsafe.compareAndSwapInt(this, valueOffset, expect, update); +} + +/** + * @param 操作对象 + * @param 对象的偏移值 + * @param 原来的值,即期望的值; + * @param 需要修改的值 + * @return {@code true} if successful. False return indicates that + * the actual value was not equal to the expected value. + */ +public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5); + +``` + + + + -- Gitee From a5d34a68112eb09a3d1e08907b362540e82a542c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=B6=E5=AD=90?= Date: Thu, 19 Dec 2019 10:41:23 +0800 Subject: [PATCH 4/7] =?UTF-8?q?=E5=88=A0=E9=99=A4=E6=96=87=E4=BB=B6=20week?= =?UTF-8?q?=5F01/09/README.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- week_01/09/README.md | 47 -------------------------------------------- 1 file changed, 47 deletions(-) delete mode 100644 week_01/09/README.md diff --git a/week_01/09/README.md b/week_01/09/README.md deleted file mode 100644 index 5bb53d0..0000000 --- a/week_01/09/README.md +++ /dev/null @@ -1,47 +0,0 @@ -## 源码刻意学习小组 - -[TOC] - - - -### 一、学习周期(2个月) - -| 时间 | 内容 | -| :------------------------------- | ------- | -| 第一周 (2019/12/09-2019/12.15) | jdk || -| 第二周 (2019/12/16-2019/12.22) | jdk || -| 第三周 (2019/12/23-2019/12.29) | jdk || -| 第四周 (2019/12/30-2020/01/05) | jdk || -| 第五周 (2020/01/06-2020/01/12) | Spring || -| 第六周 (2020/01/13-2020/01/19) | Spring || -| 第七周 (2020/01/20-2020/01/26) | MyBatis || -| 第八周 (2020/01/27-2020/02/02) | MyBatis || - - - -### 二、作业 - -#### 1、源码学习笔记(必做) - -​ 至少提交2个类的源码分析笔记 - -#### 2、本周学习总结(可选) - -​ 学习总结直接在GitHub的issue上发布即可。 - -#### 3、review5名其他的学习笔记或学习总结 - -​ 在项目的`Pull requests`可以看到其他人的Pull requests记录,并进行review。 - - - -###三、源码提交流程 -- 先将[xxx]仓库 `fork` 到自己的 GitHub 账号下。 -- 将 `fork` 后的仓库 `clone` 到本地,然后在本地新建、修改自己的源码学习笔记,**注意:** 仅允许在和自己编号对应的目录下新建或修改自己的源码学习笔记。完成后,将相关修改部分 `push` 到自己的 GitHub 远程仓库。 -- 当完成本周作业,提交 `Pull Request`申请给[xxx]仓库,Pull 作业时,必须备注自己的编号和提交第几周的作业,如`007-week 02`,是指编号为`007`的成员提交的`第二周`的源码学习笔记。 -- 源码学习笔记的命名规则:**`内容标题-编号`**,比如学号为 `007` 的成员完成`ArrayList`类后,请将源码学习笔记名保存为 `ArrayList-007 `。(内容标题自定义) -- 务必按照Pull Request的备注形式和作业文件的命名进行提交,这样方便统计。 - - - -ps:任何学习上的问题可以发布issure求助,其他同学有时间就帮忙看看哈。 \ No newline at end of file -- Gitee From 77d0b70ebd3558cc785bb29bafffee23537df9bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=B6=E5=AD=90?= Date: Thu, 19 Dec 2019 10:45:58 +0800 Subject: [PATCH 5/7] =?UTF-8?q?=E5=88=A0=E9=99=A4=E6=96=87=E4=BB=B6=20week?= =?UTF-8?q?=5F02/09/README.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- week_02/09/README.md | 1 - 1 file changed, 1 deletion(-) delete mode 100644 week_02/09/README.md diff --git a/week_02/09/README.md b/week_02/09/README.md deleted file mode 100644 index 4287ca8..0000000 --- a/week_02/09/README.md +++ /dev/null @@ -1 +0,0 @@ -# \ No newline at end of file -- Gitee From 3f08218e1501bf898f390dd20d45bc55b73c680f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=B6=E5=AD=90?= Date: Thu, 19 Dec 2019 16:22:05 +0800 Subject: [PATCH 6/7] AtomicStampedReference-009 --- week_02/09/AtomicStampedReference-009.md | 189 +++++++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100644 week_02/09/AtomicStampedReference-009.md diff --git a/week_02/09/AtomicStampedReference-009.md b/week_02/09/AtomicStampedReference-009.md new file mode 100644 index 0000000..5474475 --- /dev/null +++ b/week_02/09/AtomicStampedReference-009.md @@ -0,0 +1,189 @@ +###关于AtomicStampedReference + +1、认识ABA + +ABA问题发生在多线程环境中,当某线程连续读取同一块内存地址两次,两次得到的值一样,它简单地认为“此内存地址的值并没有被修改过”, + 然而,同时可能存在另一个线程在这两次读取之间把这个内存地址的值从A修改成了B又修改回了A,这时还简单地认为“没有修改过”显然是错误的。 + +先看一个成功执行CAS的demo,此时是存在ABA危害的: + +```java + +private static AtomicInteger atomicInt = new AtomicInteger(100); + +public static void main(String[] args) throws InterruptedException { + + Thread intT1 = new Thread(new Runnable() { + @Override + public void run(){ + atomicInt.compareAndSet(100, 101); + + int value1 = atomicInt.get(); + System.out.println("intT1 value1 :"+value1); //intT1 value1 :101 + + atomicInt.compareAndSet(101, 100); + int value2 = atomicInt.get(); + System.out.println("intT1 value2 :"+value2); //intT1 value2 :100 + } + }); + + Thread intT2 = new Thread(new Runnable() { + @Override + public void run() { + try { + TimeUnit.SECONDS.sleep(1); + } catch (InterruptedException e) { + e.getMessage(); + } + boolean c3 = atomicInt.compareAndSet(100, 101); + //多线程执行修改AtomicInteger会返回true成功执行CAS: + System.out.println("intT2 update "+c3); // intT2 update true + int value3 = atomicInt.get(); + System.out.println("intT2 value3 "+value3); //intT2 value3 101 + } + }); + + intT1.start(); + intT2.start(); + intT1.join(); + intT2.join(); +} + +``` + +又看看关于AtomicStampedReference解决ABA的demo: + +```java +//创建一个初始值为100,版本号为0的AtomicStampedReference +private static AtomicStampedReference atomicStampedRef = new AtomicStampedReference(100, 0); + +public static void main(String[] args) throws InterruptedException { + + Thread refT1 = new Thread(new Runnable() { + @Override + public void run() { + try { + TimeUnit.SECONDS.sleep(1); + } catch (InterruptedException e) { + } + atomicStampedRef.compareAndSet(100, 101, atomicStampedRef.getStamp(), atomicStampedRef.getStamp() + 1); + System.out.println("refT1 stamp1: " + atomicStampedRef.getStamp() +",value1: "+atomicStampedRef.get(new int[atomicStampedRef.getStamp()]) );//refT1 stamp1: 1,value1: 101 + atomicStampedRef.compareAndSet(101, 100, atomicStampedRef.getStamp(), atomicStampedRef.getStamp() + 1); + System.out.println("refT1 stamp2: " + atomicStampedRef.getStamp() +",value2: "+atomicStampedRef.get(new int[atomicStampedRef.getStamp()]) );//refT1 stamp2: 2,value2: 100 + } + }); + + + Thread refT2 = new Thread(new Runnable() { + @Override + public void run() { + int stamp = atomicStampedRef.getStamp(); + System.out.println("refT2 stamp1 :" + stamp);//refT2 stamp2 :2,value :100 + try { + TimeUnit.SECONDS.sleep(2); + } catch (InterruptedException e) { + } + boolean c3 = atomicStampedRef.compareAndSet(100, 101, stamp, stamp + 1); + //多线程执行update 返回 false (对于ABA问题会执行CAS失败) + System.out.println("refT2 update "+c3); // refT2 update false + int stamp1 = atomicStampedRef.getStamp(); + System.out.println("refT2 stamp2 :"+ stamp1 + ",value :"+ atomicStampedRef.get(new int[stamp1])); //refT1 stamp2: 2,value2: 100 + } + }); + + refT1.start(); + refT2.start(); +} + + +``` + +2、认识AtomicStampedReference + +1) 内部类 + +```java + +public class AtomicStampedReference { + +private static class Pair { + final T reference; + final int stamp; + private Pair(T reference, int stamp) { + this.reference = reference; + this.stamp = stamp; + } + //将元素值和版本号绑定在一起,存储在Pair的reference和stamp(邮票、戳的意思)中。 + static Pair of(T reference, int stamp) { + return new Pair(reference, stamp); + } +} +。。。。。。 + +} +``` + +2)属性 + +```java +private volatile Pair pair; + +private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe(); +//声明一个Pair类型的变量并使用Unsfae获取其偏移量,存储到pairOffset中。 +private static final long pairOffset = + objectFieldOffset(UNSAFE, "pair", AtomicStampedReference.class); +``` + +3)构造方法 + +```java +/* +* 创建一个新的AtomicStampedReference +* @param initialRef 初始值 +* @param initialStamp 初始版本 +* / +public AtomicStampedReference(V initialRef, int initialStamp) { + pair = Pair.of(initialRef, initialStamp); +} +``` + +4)compareAndSet方法 + +```java +/** + * @param expectedReference 初始值 + * @param newReference 修改新值 + * @param expectedStamp 初始版本 + * @param newStamp 新版本 + * @return 返回修改成功与否 + */ +public boolean compareAndSet(V expectedReference, + V newReference, + int expectedStamp, + int newStamp) { + //获取当前<元素值,版本号> + Pair current = pair; + return + // 引用没变 + expectedReference == current.reference && + // 版本号没变 + expectedStamp == current.stamp && + // 新引用等于旧引用 + ((newReference == current.reference && + newStamp == current.stamp) || + //构造新的Pair对象并CAS更新 + casPair(current, Pair.of(newReference, newStamp))); +} + +private boolean casPair(Pair cmp, Pair val) { + // 调用Unsafe的compareAndSwapObject()方法CAS更新pair的引用为新引用 + return UNSAFE.compareAndSwapObject(this, pairOffset, cmp, val); +} + +``` + +ps: + +(1)如果元素值和版本号都没有变化,并且和新的也相同,返回true; + +(2)如果元素值和版本号都没有变化,并且和新的不完全相同,就构造一个新的Pair对象并执行CAS更新pair \ No newline at end of file -- Gitee From cad106e5c5efd20ca1e39a6827737a1ebb29fd15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=B6=E5=AD=90?= Date: Sat, 21 Dec 2019 20:56:39 +0800 Subject: [PATCH 7/7] LongAdder-009 --- week_02/09/LongAdder-009.md | 139 ++++++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 week_02/09/LongAdder-009.md diff --git a/week_02/09/LongAdder-009.md b/week_02/09/LongAdder-009.md new file mode 100644 index 0000000..2e0452d --- /dev/null +++ b/week_02/09/LongAdder-009.md @@ -0,0 +1,139 @@ +###关于JDK1.8LongAdder + +1、认识LongAdder + +1)简介 + + JDK1.8时,java.util.concurrent.atomic包中提供了一个新的原子类:LongAdder。 + 根据Oracle官方文档的介绍,LongAdder在高并发的场景下会比它的前辈————AtomicLong 具有更好的性能,代价是消耗更多的内存空间: + +2)为什么要引入LongAdder + +3)引入 LongAdder是否可以替代AtomicLong + + LongAdder提供的API和AtomicLong比较接近,两者都能以原子的方式对long型变量进行增减。但是AtomicLong提供的功能其实更丰富,尤其是addAndGet、decrementAndGet、compareAndSet这些方法。 + addAndGet、decrementAndGet除了单纯的做自增自减外,还可以立即获取增减后的值,而LongAdder则需要做同步控制才能精确获取增减后的值。如果业务需求需要精确的控制计数,做计数比较,AtomicLong也更合适。 + 另外,从空间方面考虑,LongAdder其实是一种“空间换时间”的思想,从这一点来讲AtomicLong更适合。当然,如果你一定要跟我杠现代主机的内存对于这点消耗根本不算什么,那我也办法。 + 总之,低并发、一般的业务场景下AtomicLong是足够了。如果并发量很多,存在大量写多读少的情况,那LongAdder可能更合适。适合的才是最好的,如果真出现了需要考虑到底用AtomicLong好还是LongAdder的业务场景,那么这样的讨论是没有意义的,因为这种情况下要么进行性能测试,以准确评估在当前业务场景下两者的性能,要么换个思路寻求其它解决方案。 + + +2、原理 + +LongAdder内部没有什么特殊,只有一个空的构造器 + +```java +//创建一个LongAdder. +public LongAdder() { +} +``` + +接着深入看,主要的复杂实现都在父类的Striped64 + +```java +abstract class Striped64 extends Number { + // Unsafe mechanics + private static final sun.misc.Unsafe UNSAFE; + private static final long BASE; + private static final long CELLSBUSY; + private static final long PROBE; + //初始化通过Unsafe获取到类字段的偏移量 + static { + try { + UNSAFE = sun.misc.Unsafe.getUnsafe(); + Class sk = Striped64.class; + BASE = UNSAFE.objectFieldOffset + (sk.getDeclaredField("base")); + CELLSBUSY = UNSAFE.objectFieldOffset + (sk.getDeclaredField("cellsBusy")); + Class tk = Thread.class; + PROBE = UNSAFE.objectFieldOffset + (tk.getDeclaredField("threadLocalRandomProbe")); + } catch (Exception e) { + throw new Error(e); + } + 仅一个空构造器 +Striped64() {} + +} +``` + +除此之外,还有 + +```java +//定义一个Cell 内部类 +//这就是我们之前所说的槽,每个Cell对象存有一个value值,可以通过Unsafe来CAS操作它的值: +@sun.misc.Contended static final class Cell { + volatile long value; + Cell(long x) { value = x; } + final boolean cas(long cmp, long val) { + return UNSAFE.compareAndSwapLong(this, valueOffset, cmp, val); + } + + // Unsafe mechanics + private static final sun.misc.Unsafe UNSAFE; + private static final long valueOffset; + static { + try { + UNSAFE = sun.misc.Unsafe.getUnsafe(); + Class ak = Cell.class; + valueOffset = UNSAFE.objectFieldOffset + (ak.getDeclaredField("value")); + } catch (Exception e) { + throw new Error(e); + } + } +} + +//cpu核数,决定槽数组大小 +static final int NCPU = Runtime.getRuntime().availableProcessors(); + +/** + * 用来定义槽数组,大小为2的幂次(看到Cell[]就是之前提到的槽数组,base就是非并发条件下的基数累计值。) + */ +transient volatile Cell[] cells; + +/** + * 基数值: + * 1、没有遇到并发线程竞争,则直接使用base累加值 + * 2、初始化cells数组时,保证cells数组只被初始化了一次(只有一次线程可以对cells初始化)其它竞争失败的线程数值会累积到base上 + */ +transient volatile long base; + +/** + * 锁标识 :1-加锁;0-无锁状态 + */ +transient volatile int cellsBusy; + +``` + +3、LongAdder核心方法 + + 1)add(long x) + +```java +//可以使LongAdder中存储的值增加x,x可为正可为负。 +public void add(long x) { + // as是Striped64中的cells属性 + // b是Striped64中的base属性 + // v是当前线程hash到的Cell中存储的值 + // m是cells的长度减1,hash时作为掩码使用 + // a是当前线程hash到的Cell + Cell[] as; long b, v; int m; Cell a; + // 条件1:cells不为空,说明出现过竞争,cells已经创建 + // 条件2:cas操作base失败,说明其它线程先一步修改了base,正在出现竞争 + if ((as = cells) != null || !casBase(b = base, b + x)) { + // true表示当前竞争还不激烈 + // false表示竞争激烈,多个线程hash到同一个Cell,可能要扩容 + boolean uncontended = true; + // 条件1:cells为空,说明正在出现竞争,上面是从条件2过来的 + // 条件2:应该不会出现 + // 条件3:当前线程所在的Cell为空,说明当前线程还没有更新过Cell,应初始化一个Cell + // 条件4:更新当前线程所在的Cell失败,说明现在竞争很激烈,多个线程hash到了同一个Cell,应扩容 + if (as == null || (m = as.length - 1) < 0 || + (a = as[getProbe() & m]) == null || + !(uncontended = a.cas(v = a.value, v + x))) + // 调用Striped64中的方法处理 + longAccumulate(x, null, uncontended); + } +} +``` -- Gitee