From a28c358f7363e8912f02a471729781bc1fb2de52 Mon Sep 17 00:00:00 2001 From: ConanJu_pc Date: Sun, 22 Dec 2019 10:37:46 +0800 Subject: [PATCH 1/4] unsafe --- week_02/42/Unsafe.md | 250 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 250 insertions(+) create mode 100644 week_02/42/Unsafe.md diff --git a/week_02/42/Unsafe.md b/week_02/42/Unsafe.md new file mode 100644 index 0000000..03abc48 --- /dev/null +++ b/week_02/42/Unsafe.md @@ -0,0 +1,250 @@ +/** + * 被final修饰,不允许被继承 + * allocateMemory 分配内存空间设置值之后如何判断指定索引处数据不存在? + */ +public final class Unsafe { + private static final Unsafe theUnsafe; + + // 有何作用? + private static native void registerNatives(); + + // 构造函数私有,无法在外部直接实例化 + private Unsafe() { + } + + // 静态方法,获取Unsafe实例,但是在外部调用该方法会直接抛出异常 + // 因此我们只能通过反射的方式获取Unsafe类 + @CallerSensitive + public static Unsafe getUnsafe() { + Class var0 = Reflection.getCallerClass(); + if (!VM.isSystemDomainLoader(var0.getClassLoader())) { + throw new SecurityException("Unsafe"); + } else { + return theUnsafe; + } + } + + /** + * 通过反射的方式获取Unsafe 类(自我实现该方法,并不是源码), + * 不能使用反射调用构造方法获取实例对象,也会报异常。 + */ + public static Unsafe initUnsafe() { + Field field = Unsafe.class.getDeclaredField("theUnsafe"); + field.setAccessible(true); + Unsafe unsafeObj = (Unsafe) field.get(null); + } + + + /** + * 为对象分配内存空间,并不会直接调用构造方法。 + * 编写测试类,可以验证。 + */ + public native Object allocateInstance(Class var1) throws InstantiationException; + + class Person { + int age = 0; + public Person() { + age = 20; + } + public int getAge() { + return age; + } + public void setAge(int age) { + this.age = age; + } + } + + /** + * 在这里不会答应20,会答应0的结果,说明并不会调用父类的默认构造方法。 + */ + private static void allocationInstance() throws Exception { + Field field = Unsafe.class.getDeclaredField("theUnsafe"); + field.setAccessible(true); + Unsafe unsafeObj = (Unsafe) field.get(null); + Person person = (Person) unsafeObj.allocateInstance(Person.class); + System.out.println(person.getAge()); + } + + /** + * 如下 put 方法都能修改任意私有字段值 + * 下面是测试方法 + */ + private static void putSomeKey() throws Exception { + Field field = Unsafe.class.getDeclaredField("theUnsafe"); + field.setAccessible(true); + Unsafe unsafeObj = (Unsafe) field.get(null); + Person person = new Person(); + Field personName = person.getClass().getDeclaredField("name"); + System.out.println(unsafeObj.objectFieldOffset(personName)); + /** + * JVM的实现可以自由选择如何实现Java对象的“布局”, + * 也就是在内存里Java对象的各个部分放在哪里,包括对象的实例字段和一些元数据之类。 + * sun.misc.Unsafe里关于对象字段访问的方法把对象布局抽象出来, + * 它提供了objectFieldOffset()方法用于获取某个字段相对Java对象的“起始地址”的偏移量, + * 也提供了getInt、getLong、getObject之类的方法可以使用前面获取的偏移量来访问某个Java对象的某个字段。 + */ + unsafeObj.putObject(person, unsafeObj.objectFieldOffset(personName), "conanju"); + System.out.println(unsafeObj.objectFieldOffset(personName)); + System.out.println(person.getName()); + } + public native void putInt(Object var1, long var2, int var4); + public native void putObject(Object var1, long var2, Object var4); + public native void putBoolean(Object var1, long var2, boolean var4); + public native void putByte(Object var1, long var2, byte var4); + public native void putShort(Object var1, long var2, short var4); + public native void putChar(Object var1, long var2, char var4); + public native void putLong(Object var1, long var2, long var4); + public native void putFloat(Object var1, long var2, float var4); + public native void putDouble(Object var1, long var2, double var4); + public native void putByte(long var1, byte var3); + public native void putShort(long var1, short var3); + public native void putChar(long var1, char var3); + public native void putInt(long var1, int var3); + public native void putLong(long var1, long var3); + public native void putFloat(long var1, float var3); + public native void putDouble(long var1, double var3); + public native void putAddress(long var1, long var3); + + + + public native int getInt(Object var1, long var2); + public native Object getObject(Object var1, long var2); + public native boolean getBoolean(Object var1, long var2); + public native byte getByte(Object var1, long var2); + public native short getShort(Object var1, long var2); + public native char getChar(Object var1, long var2); + public native long getLong(Object var1, long var2); + public native float getFloat(Object var1, long var2); + public native double getDouble(Object var1, long var2); + public native byte getByte(long var1); + public native short getShort(long var1); + public native char getChar(long var1); + public native int getInt(long var1); + public native long getLong(long var1); + public native float getFloat(long var1); + public native double getDouble(long var1); + public native long getAddress(long var1); + + /** + * 调用allocateMemory分配内存空间 + * 使用Unsafe的allocateMemory()我们可以直接在堆外分配内存, + * 这可能非常有用,但我们要记住,这个内存不受JVM管理,因此我们要调用freeMemory()方法手动释放它。 + * 假设我们要在堆外创建一个巨大的int数组,我们可以使用allocateMemory()方法来实现,可以无限分配空间。 + * + */ + 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); + + public native long staticFieldOffset(Field var1); + public native long objectFieldOffset(Field var1); + public native Object staticFieldBase(Field var1); + public native boolean shouldBeInitialized(Class var1); + public native void ensureClassInitialized(Class var1); + public native int arrayBaseOffset(Class var1); + public native int arrayIndexScale(Class var1); + public native int addressSize(); + public native int pageSize(); + 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 void throwException(Throwable var1); + + /** + * cas操作,compare and swap 简单的说就是对比被操作的元素,是否在多线程的情况存在修改 + * 没有则返回true ,存在则返回false + */ + 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); + + public native Object getObjectVolatile(Object var1, long var2); + public native void putObjectVolatile(Object var1, long var2, Object var4); + public native int getIntVolatile(Object var1, long var2); + public native void putIntVolatile(Object var1, long var2, int var4); + public native boolean getBooleanVolatile(Object var1, long var2); + public native void putBooleanVolatile(Object var1, long var2, boolean var4); + public native byte getByteVolatile(Object var1, long var2); + public native void putByteVolatile(Object var1, long var2, byte var4); + public native short getShortVolatile(Object var1, long var2); + public native void putShortVolatile(Object var1, long var2, short var4); + public native char getCharVolatile(Object var1, long var2); + public native void putCharVolatile(Object var1, long var2, char var4); + public native long getLongVolatile(Object var1, long var2); + public native void putLongVolatile(Object var1, long var2, long var4); + public native float getFloatVolatile(Object var1, long var2); + public native void putFloatVolatile(Object var1, long var2, float var4); + public native double getDoubleVolatile(Object var1, long var2); + public native void putDoubleVolatile(Object var1, long var2, double var4); + public native void putOrderedObject(Object var1, long var2, Object var4); + + public native void putOrderedInt(Object var1, long var2, int var4); + + public native void putOrderedLong(Object var1, long var2, long var4); + + public native void unpark(Object var1); + + public native void park(boolean var1, long var2); + + public native int getLoadAverage(double[] var1, int var2); + + public final int getAndAddInt(Object var1, long var2, int var4) { + int var5; + do { + var5 = this.getIntVolatile(var1, var2); + } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4)); + + return var5; + } + + public final long getAndAddLong(Object var1, long var2, long var4) { + long var6; + do { + var6 = this.getLongVolatile(var1, var2); + } while(!this.compareAndSwapLong(var1, var2, var6, var6 + var4)); + + return var6; + } + + public final int getAndSetInt(Object var1, long var2, int var4) { + int var5; + do { + var5 = this.getIntVolatile(var1, var2); + } while(!this.compareAndSwapInt(var1, var2, var5, var4)); + + return var5; + } + + public final long getAndSetLong(Object var1, long var2, long var4) { + long var6; + do { + var6 = this.getLongVolatile(var1, var2); + } while(!this.compareAndSwapLong(var1, var2, var6, var4)); + + return var6; + } + + public final Object getAndSetObject(Object var1, long var2, Object var4) { + Object var5; + do { + var5 = this.getObjectVolatile(var1, var2); + } while(!this.compareAndSwapObject(var1, var2, var5, var4)); + + return var5; + } +} -- Gitee From a05d672129841d339f1712d61651d1cae6b32e93 Mon Sep 17 00:00:00 2001 From: ConanJu_pc Date: Sun, 22 Dec 2019 15:22:51 +0800 Subject: [PATCH 2/4] AtomicInteger AtomicInteger --- week_02/42/AtomicInteger.md | 228 ++++++++++++++++++++++++++++++++++++ 1 file changed, 228 insertions(+) create mode 100644 week_02/42/AtomicInteger.md diff --git a/week_02/42/AtomicInteger.md b/week_02/42/AtomicInteger.md new file mode 100644 index 0000000..572a5aa --- /dev/null +++ b/week_02/42/AtomicInteger.md @@ -0,0 +1,228 @@ +/** + * 原子操作,java.util.concurrent.atomic 下面包含了AtomicBoolean、AtomicLong、AtomicReference等类 + * 这些类所执行的操作都是针对原子性的,引用化学里面的一段话就是说原子就是不可在分割的元素。 + * 所有这些操作保证了线程安全性。 + */ +public class AtomicInteger extends Number implements java.io.Serializable { + private static final long serialVersionUID = 6214790243416807050L; + + // setup to use Unsafe.compareAndSwapInt for updates + /** + * 获取unsafe类 + */ + private static final Unsafe unsafe = Unsafe.getUnsafe(); + /** + * 偏移量( 用于后续cas 操作) ,前面学习Unsafe的时候我们也知道 + * 使用compareAndSet方法的时候都会用到偏移量 + */ + private static final long valueOffset; + + /** + * 静态方法,通过unsafe对象获取value字段的偏移量并赋值给valueOffset + */ + static { + try { + valueOffset = unsafe.objectFieldOffset + (AtomicInteger.class.getDeclaredField("value")); + } catch (Exception ex) { throw new Error(ex); } + } + + /** + * 使用volatile修饰,保证内存可见性,防止重排序 + * 存储int类型值的地方 + */ + private volatile int value; + + + public AtomicInteger(int initialValue) { + value = initialValue; + } + + /** + * Creates a new AtomicInteger with initial value {@code 0}. + * 空构造函数,创建一个0的AtomicInteger 注意不是null + */ + public AtomicInteger() { + } + + + public final int get() { + return value; + } + + + public final void set(int newValue) { + value = newValue; + } + + + /** + * 不同于其他set,其他set直接调用变量值,改变量使用volatile修饰, + * 在多线程的情况下,可以保证内存可见性, + * 但是lazySet,顾名思义却不能保证这一点,也就是说在多线程情况下,不会立马可见 + * @param newValue + */ + public final void lazySet(int newValue) { + unsafe.putOrderedInt(this, valueOffset, newValue); + } + + /** + * 调用unsafe方法,设置值返回旧值,所有操作都会用到偏移量 + */ + public final int getAndSet(int newValue) { + return unsafe.getAndSetInt(this, valueOffset, newValue); + } + + /** + * 对比当前偏移量的值是否是期望值,如果是则更新并返回true, + * 反之则返回false + * @param expect + * @param update + * @return + */ + public final boolean compareAndSet(int expect, int update) { + return unsafe.compareAndSwapInt(this, valueOffset, expect, update); + } + + /** + * 为何有两个不一样的方法,但是实现都是一样的呢? + */ + public final boolean weakCompareAndSet(int expect, int update) { + return unsafe.compareAndSwapInt(this, valueOffset, expect, update); + } + + /** + * 获取当前值并且增加1 + * @return + */ + public final int getAndIncrement() { + return unsafe.getAndAddInt(this, valueOffset, 1); + } + + /** + * 获取当前值并且-1 + * @return + */ + public final int getAndDecrement() { + return unsafe.getAndAddInt(this, valueOffset, -1); + } + + /** + * 获取当前值,并且增加指定值 + * @param delta + * @return + */ + public final int getAndAdd(int delta) { + return unsafe.getAndAddInt(this, valueOffset, delta); + } + + /** + * 加1并且返回+1之后的结果。如果原始是1,那么调用该方法之后value值为2,返回的也是2 + * @return + */ + public final int incrementAndGet() { + return unsafe.getAndAddInt(this, valueOffset, 1) + 1; + } + + /** + * 当前值-1 并且返回-1之后的结果 + * @return + */ + public final int decrementAndGet() { + return unsafe.getAndAddInt(this, valueOffset, -1) - 1; + } + + /** + * 当前值+N,并且返回+n之后的结果 + * @param delta + * @return + */ + public final int addAndGet(int delta) { + return unsafe.getAndAddInt(this, valueOffset, delta) + delta; + } + + + /** + * 使用IntBinaryOperator 函数式变成,对当前值进行计算,并更新当前值,返回计算前的旧值 + * 如下测试代码,会增加10 + AtomicInteger atomicInteger = new AtomicInteger(); + System.out.println(atomicInteger.updateAndGet(operand -> { + operand += 10; + return operand; + })); + * @return + */ + public final int getAndUpdate(IntUnaryOperator updateFunction) { + int prev, next; + do { + prev = get(); + next = updateFunction.applyAsInt(prev); + } while (!compareAndSet(prev, next)); + return prev; + } + + /** + * 先更新再获取更新之后的值 + */ + public final int updateAndGet(IntUnaryOperator updateFunction) { + int prev, next; + do { + prev = get(); + next = updateFunction.applyAsInt(prev); + } while (!compareAndSet(prev, next)); + return next; + } + + /** + * 函数式操作,可以通过函数式接口,如果是除法运算则是被除数。 + * 但是如果是除法运算,x不能等于0 + */ + public final int getAndAccumulate(int x, + IntBinaryOperator accumulatorFunction) { + int prev, next; + do { + prev = get(); + next = accumulatorFunction.applyAsInt(prev, x); + } while (!compareAndSet(prev, next)); + return prev; + } + + + public final int accumulateAndGet(int x, + IntBinaryOperator accumulatorFunction) { + int prev, next; + do { + prev = get(); + next = accumulatorFunction.applyAsInt(prev, x); + } while (!compareAndSet(prev, next)); + return next; + } + + /** + * Returns the String representation of the current value. + * @return the String representation of the current value + */ + public String toString() { + return Integer.toString(get()); + } + + public int intValue() { + return get(); + } + + + public long longValue() { + return (long)get(); + } + + + public float floatValue() { + return (float)get(); + } + + + public double doubleValue() { + return (double)get(); + } + +} -- Gitee From 205dab95f83695bfaef66d087f418a5b158944f7 Mon Sep 17 00:00:00 2001 From: ConanJu_pc Date: Sun, 22 Dec 2019 20:05:35 +0800 Subject: [PATCH 3/4] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=96=87=E4=BB=B6=20?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- week_02/42/AtomicReference.md | 121 ++++++++++++++++++++++++++++ week_02/42/TestAtomicReference.java | 116 ++++++++++++++++++++++++++ 2 files changed, 237 insertions(+) create mode 100644 week_02/42/AtomicReference.md create mode 100644 week_02/42/TestAtomicReference.java diff --git a/week_02/42/AtomicReference.md b/week_02/42/AtomicReference.md new file mode 100644 index 0000000..08cd821 --- /dev/null +++ b/week_02/42/AtomicReference.md @@ -0,0 +1,121 @@ +/** + * 可以很好的解决ABA问题, + * 那么什么是ABA问题呢? + * 什么是ABA?如果在算法中的节点可以被循环使用, + * 那么在使用“比较并交换”指令就可能出现这种问题, + * 在CAS操作中将判断“V的值是否仍然为A?”, + * 并且如果是的话就继续执行更新操作,在某些算法中, + * 如果V的值首先由A变为B,再由B变为A,那么CAS将会操作成功 + * 那么是如何进行规避ABA问题的呢? + * + */ +public class AtomicStampedReference { + private volatile AtomicStampedReference.Pair pair; + private static final Unsafe UNSAFE = Unsafe.getUnsafe(); + private static final long pairOffset; + + + public AtomicStampedReference(V var1, int var2) { + this.pair = AtomicStampedReference.Pair.of(var1, var2); + } + + public V getReference() { + return this.pair.reference; + } + + public int getStamp() { + return this.pair.stamp; + } + + public V get(int[] var1) { + AtomicStampedReference.Pair var2 = this.pair; + var1[0] = var2.stamp; + return var2.reference; + } + + public boolean weakCompareAndSet(V var1, V var2, int var3, int var4) { + return this.compareAndSet(var1, var2, var3, var4); + } + + /** + * + * @param var1 expectedReference + * @param var2 newReference + * @param var3 expectedStamp + * @param var4 newStamp + * @return + */ + public boolean compareAndSet(V var1, V var2, int var3, int var4) { + // 获取当前对象的Pair信息 + AtomicStampedReference.Pair var5 = this.pair; + return var1 == var5.reference // 引用与当前对象一致,也就是说中途发生了变化(ABA)情况下这里直接返回false + && var3 == var5.stamp // 引用戳与当前对象一致 + && ( // 新的值(引用和引用戳都与当前对象一致) + // 这里是或运算,只要被更新的不变则直接返回了 + var2 == var5.reference && var4 == var5.stamp + // 如果有变化,则执行casPair方法 + || this.casPair(var5, AtomicStampedReference.Pair.of(var2, var4)) + ); + } + + /** + * 调用unsafe compareAndSwapObject 方法更新值。 + * @param var1 + * @param var2 + * @return + */ + private boolean casPair(AtomicStampedReference.Pair var1, AtomicStampedReference.Pair var2) { + return UNSAFE.compareAndSwapObject(this, pairOffset, var1, var2); + } + + public void set(V var1, int var2) { + AtomicStampedReference.Pair var3 = this.pair; + if (var1 != var3.reference || var2 != var3.stamp) { + this.pair = AtomicStampedReference.Pair.of(var1, var2); + } + + } + + public boolean attemptStamp(V var1, int var2) { + AtomicStampedReference.Pair var3 = this.pair; + return var1 == var3.reference && (var2 == var3.stamp || this.casPair(var3, AtomicStampedReference.Pair.of(var1, var2))); + } + + + + static long objectFieldOffset(Unsafe var0, String var1, Class var2) { + try { + return var0.objectFieldOffset(var2.getDeclaredField(var1)); + } catch (NoSuchFieldException var5) { + NoSuchFieldError var4 = new NoSuchFieldError(var1); + var4.initCause(var5); + throw var4; + } + } + + /** + * 申明一个变量并且获取其偏移量,将偏移量存储到pairOffset + */ + static { + pairOffset = objectFieldOffset(UNSAFE, "pair", AtomicStampedReference.class); + } + + /** + * 内部类,定义引用以及引用戳(我这么理解的) + * 将元素与引用戳绑定在一起,相当于唯一键的形式 + * @param + */ + private static class Pair { + final T reference; // 引用 + final int stamp; // 引用戳,同一个应用保证具有唯一的戳 + + private Pair(T var1, int var2) { + this.reference = var1; + this.stamp = var2; + } + + static AtomicStampedReference.Pair of(T var0, int var1) { + return new AtomicStampedReference.Pair(var0, var1); + } + } +} diff --git a/week_02/42/TestAtomicReference.java b/week_02/42/TestAtomicReference.java new file mode 100644 index 0000000..4da55e6 --- /dev/null +++ b/week_02/42/TestAtomicReference.java @@ -0,0 +1,116 @@ +package com.jdk.week2; + + +import java.util.concurrent.atomic.AtomicReference; + +/** + * Copyright + * + * @author conanju + * @since 2019/12/22 18:00 + */ +public class TestAtomicReference { + static class Stack { + + private AtomicReference first = new AtomicReference<>(); + + static class Node { + int value; + Node next; + + public Node(int value) { + this.value = value; + } + } + + /** + * 模拟出栈操作 + * + * @return + */ + public Node pop() { + while (true) { + // 获取顶节点 + Node top = first.get(); + // 顶节点为空,直接返回为空 + if (top == null) { + return null; + } + // 获取顶节点的next节点。 + Node next = top.next; + // 更新顶节点指向顶节点的next节点 + if (first.compareAndSet(top, next)) { + // 原始顶节点元素next指向为空 + top.next = null; + // 返回顶节点元素,弹出顶点元素 + return top; + } + } + } + + + /** + * 模拟入栈操作 + * + * @return + */ + public void push(Node newTop) { + while (true) { + // 获取顶节点 + Node oldTop = first.get(); + // 更新新顶节点的next节点为原始顶节点 + newTop.next = oldTop; + // 更新顶节点指向新的顶节点 + if (first.compareAndSet(oldTop, newTop)) { + return; + } + } + } + + + } + + + private static void test() { + Stack stack = new Stack(); + stack.push(new Stack.Node(3)); + stack.push(new Stack.Node(2)); + stack.push(new Stack.Node(1)); + Thread threadA = new Thread(() -> { + /** + * 多线程调试,线程还未执行此处时让下面的线程先执行。 + * 等待线程2执行完毕之后,在释放断点执行线程1,会打印2 + */ + System.out.println(stack.pop().value); + /** + * 按理说在正常情况下,下面代码打印应该是3,但是实际在多线程的操作下,打印的是2 + * 线程1恢复执行,比较节点1的引用并没有改变,执行CAS成功,此时栈变为 top->2; + * 那是因为线程1在第一步保存的next是节点2,所以它执行CAS成功后top节点就指向了节点2了。 + */ + System.out.println(stack.pop().value); + }); + + Thread threadB = new Thread(() -> { + // 会先弹出1 + Stack.Node nodeA = stack.pop(); + System.out.println(nodeA.value); + // 在弹出2 + Stack.Node nodeB = stack.pop(); + System.out.println(nodeB.value); + // 添加了1,变成了只有 top -> 1 ->3 + stack.push(nodeA); + }); + + + threadA.setName("threadA"); + threadA.start(); + threadB.setName("threadB"); + threadB.start(); + + } + + public static void main(String[] args) { + test(); + } + +} -- Gitee From 35d01e17788708cefb1adfc1ad53c4f323a8f6ee Mon Sep 17 00:00:00 2001 From: ConanJu_pc Date: Sun, 22 Dec 2019 20:36:23 +0800 Subject: [PATCH 4/4] =?UTF-8?q?LongAdder=20LongAdder=20=E8=BF=98=E6=98=AF?= =?UTF-8?q?=E6=9C=89=E7=82=B9=E6=9C=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- week_02/42/LongAdder.md | 162 ++++++++++++++++++++++++++++++++++ week_02/42/TestLongAdder.java | 10 +++ 2 files changed, 172 insertions(+) create mode 100644 week_02/42/LongAdder.md create mode 100644 week_02/42/TestLongAdder.java diff --git a/week_02/42/LongAdder.md b/week_02/42/LongAdder.md new file mode 100644 index 0000000..49e5f31 --- /dev/null +++ b/week_02/42/LongAdder.md @@ -0,0 +1,162 @@ +/** + * (1)LongAdder的原理是,在最初无竞争时,只更新base的值, + * 当有多线程竞争时通过分段的思想,让不同的线程更新不同的段, + * 最后把这些段相加就得到了完整的LongAdder存储的值。 + * 这样子做效率肯定要比AtomicLong性能高很多, + * AtomicLong在执行的时候会存在一定的等待(compare)直到满足条件了才设置 + * (2)继承了Striped64类,Striped64中定义了Cell内部类和各重要属性 + */ +public class LongAdder extends Striped64 implements Serializable { + private static final long serialVersionUID = 7249069246863182397L; + + public LongAdder() { + } + + public void add(long var1) { + Cell[] var3; // Striped64 的cells属性,在下面赋值 + long var4; // this.base 的值 + /** + * (1)Striped64 的cells 不为空则说明创建过cell对象,出现过竞争 + * (2)cas操作base失败,说明其它线程先一步修改了base,正在出现竞争 + */ + if ((var3 = this.cells) != null || !this.casBase(var4 = this.base, var4 + var1)) { + + /** + * true表示当前竞争还不激烈 + * false表示竞争激烈,多个线程hash到同一个Cell,可能要扩容 + * 具体查阅longAccumulate方法,false if CAS failed before call + */ + boolean var10 = true; + long var6; + int var8; + Cell var9; + if (var3 == null // Striped64 的cells属性为空, + || (var8 = var3.length - 1) < 0 // 不知道什么情况下为length-1 + || (var9 = var3[getProbe() & var8]) == null // + || !(var10 = var9.cas(var6 = var9.value, var6 + var1)) + ) { + /** + * 调用Striped64中的方法处理 + */ + this.longAccumulate(var1, (LongBinaryOperator) null, var10); + } + } + + } + + /** + * 属于Striped64 的方法,进行base操作其实就是调用unsafe的compareAndSwapLong + * 机遇BASE属性的偏移量进行操作。 + */ + final boolean casBase(long cmp, long val) { + return UNSAFE.compareAndSwapLong(this, BASE, cmp, val); + } + + /** + * Returns the probe value for the current thread. + * Duplicated from ThreadLocalRandom because of packaging restrictions. + * 属于Striped64 方法,返回的是线程中的threadLocalRandomProbe字段 + * 它是通过随机数生成的一个值,对于一个确定的线程这个值是固定的 + * 除非刻意修改它 + */ + static final int getProbe() { + return UNSAFE.getInt(Thread.currentThread(), PROBE); + } + + public void increment() { + this.add(1L); + } + + public void decrement() { + this.add(-1L); + } + + public long sum() { + Cell[] var1 = this.cells; + long var3 = this.base; + if (var1 != null) { + for (int var5 = 0; var5 < var1.length; ++var5) { + Cell var2; + if ((var2 = var1[var5]) != null) { + var3 += var2.value; + } + } + } + + return var3; + } + + public void reset() { + Cell[] var1 = this.cells; + this.base = 0L; + if (var1 != null) { + for (int var3 = 0; var3 < var1.length; ++var3) { + Cell var2; + if ((var2 = var1[var3]) != null) { + var2.value = 0L; + } + } + } + + } + + public long sumThenReset() { + Cell[] var1 = this.cells; + long var3 = this.base; + this.base = 0L; + if (var1 != null) { + for (int var5 = 0; var5 < var1.length; ++var5) { + Cell var2; + if ((var2 = var1[var5]) != null) { + var3 += var2.value; + var2.value = 0L; + } + } + } + + return var3; + } + + public String toString() { + return Long.toString(this.sum()); + } + + public long longValue() { + return this.sum(); + } + + public int intValue() { + return (int) this.sum(); + } + + public float floatValue() { + return (float) this.sum(); + } + + public double doubleValue() { + return (double) this.sum(); + } + + private Object writeReplace() { + return new LongAdder.SerializationProxy(this); + } + + private void readObject(ObjectInputStream var1) throws InvalidObjectException { + throw new InvalidObjectException("Proxy required"); + } + + private static class SerializationProxy implements Serializable { + private static final long serialVersionUID = 7249069246863182397L; + private final long value; + + SerializationProxy(LongAdder var1) { + this.value = var1.sum(); + } + + private Object readResolve() { + LongAdder var1 = new LongAdder(); + var1.base = this.value; + return var1; + } + } +} diff --git a/week_02/42/TestLongAdder.java b/week_02/42/TestLongAdder.java new file mode 100644 index 0000000..584cd44 --- /dev/null +++ b/week_02/42/TestLongAdder.java @@ -0,0 +1,10 @@ +package com.jdk.week2; + +/** + * Copyright + * + * @author conanju + * @since 2019/12/22 20:07 + */ +public class TestLongAdder { +} -- Gitee