From a1f9fa3b2f1afb0611a1903591cb74c9e5e64e0e Mon Sep 17 00:00:00 2001 From: jiangyu_1013 <283810473@qq.com> Date: Sun, 15 Mar 2020 16:13:19 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=E6=BA=90=E7=A0=81=E7=AC=94=E8=AE=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- second/week_02/36/AtomicInteger.md | 162 +++++++++++++++++++++++++++++ second/week_02/36/Unsafe.md | 135 ++++++++++++++++++++++++ 2 files changed, 297 insertions(+) create mode 100644 second/week_02/36/AtomicInteger.md create mode 100644 second/week_02/36/Unsafe.md diff --git a/second/week_02/36/AtomicInteger.md b/second/week_02/36/AtomicInteger.md new file mode 100644 index 0000000..24ab8b1 --- /dev/null +++ b/second/week_02/36/AtomicInteger.md @@ -0,0 +1,162 @@ + +# AtomicInteger 原子整数 +##### 可以原子更新的int值。用于原子递增计数器等应用程序中,不能用作java.lang.Integer的替换。扩展了Number。 + +# 1.继承关系: +``` 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; + + // 得 value 的偏移量 + static { + try { + valueOffset = unsafe.objectFieldOffset + (AtomicInteger.class.getDeclaredField("value")); + } catch (Exception ex) { throw new Error(ex); } + } + + // 用于存储 int 的值。 + private volatile int value; +``` + +# 3.方法: + +``` java + // 构造 + public AtomicInteger(int initialValue) { + value = initialValue; + } + + public AtomicInteger() { + } + + public final int get() { + return value; + } + + public final void set(int newValue) { + value = newValue; + } + + // set的延迟实现,不保证值的改变立即被其他线程看到。 + public final void lazySet(int newValue) { + unsafe.putOrderedInt(this, valueOffset, newValue); + } + + // 原子地设置为给定值并返回旧值。 + public final int getAndSet(int newValue) { + return unsafe.getAndSetInt(this, valueOffset, newValue); + } + + // compareAndSwapInt方法参数:this操作的对象;valueOffset对象中字段的偏移量;expect原来的值,即期望的值;update要修改的值; + 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 。 参数:this操作的对象;valueOffset对象中字段的偏移量; 1 要增加的值。 + // 返回的是未自增前的值。 + public final int getAndIncrement() { + return unsafe.getAndAddInt(this, valueOffset, 1); + } + + // 原子性地自减 1 。 返回的是未自减前的值。 + public final int getAndDecrement() { + return unsafe.getAndAddInt(this, valueOffset, -1); + } + + // 原子地将给定值添加到当前值。 + public final int getAndAdd(int delta) { + return unsafe.getAndAddInt(this, valueOffset, delta); + } + + // 原子性地自增 1 。 返回的是自增后的值。 + public final int incrementAndGet() { + return unsafe.getAndAddInt(this, valueOffset, 1) + 1; + } + + // // 原子性地自减 1 。 返回的是自减后的值。 + public final int decrementAndGet() { + return unsafe.getAndAddInt(this, valueOffset, -1) - 1; + } + + // 原子地将给定值添加到当前值。 + public final int addAndGet(int delta) { + return unsafe.getAndAddInt(this, valueOffset, delta) + delta; + } + + // 原子地用应用给定函数的结果更新当前值,并返回上一个值。 + 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; + } + + // 原子性地使用将给定函数应用于当前值和给定值的结果更新当前值,并返回上一个值。 + 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; + } + + // 转为字符串。 + public String toString() { + return Integer.toString(get()); + } + + // 得int、long、float、double值。 + public int intValue() { + return get(); + } + + public long longValue() { + return (long)get(); + } + + public float floatValue() { + return (float)get(); + } + + public double doubleValue() { + return (double)get(); + } +``` \ No newline at end of file diff --git a/second/week_02/36/Unsafe.md b/second/week_02/36/Unsafe.md new file mode 100644 index 0000000..3f54973 --- /dev/null +++ b/second/week_02/36/Unsafe.md @@ -0,0 +1,135 @@ + +# AtomicInteger 原子整数 +##### 可以原子更新的int值。用于原子递增计数器等应用程序中,不能用作java.lang.Integer的替换。扩展了Number。 + +# 1.继承关系: +``` java + public final class Unsafe +``` + +# 2. 属性、代码块: +``` java + // + private static final Unsafe theUnsafe; + + private static native void registerNatives(); +``` + +# 3.方法: + +``` java + // 构造函数 + private Unsafe() { + } + + // 初始化的代码主要包括调用JVM本地方法registerNatives()和sun.reflect.Reflection#registerMethodsToFilter。 + // 然后新建一个Unsafe实例命名为theUnsafe,通过静态方法getUnsafe()获取,获取的时候需要做权限判断。由此可见,Unsafe使用了单例设计(可见构造私有化了)。 + // Unsafe类做了限制,如果是普通的调用的话,它会抛出一个SecurityException异常;只有由主类加载器(BootStrap classLoader)加载的类才能调用这个类中的方法。 + + @CallerSensitive // 获取的时候需要做权限判断 + public static Unsafe getUnsafe() { + Class var0 = Reflection.getCallerClass(); + if (!VM.isSystemDomainLoader(var0.getClassLoader())) { + throw new SecurityException("Unsafe"); + } else { + return theUnsafe; + } + } + + Field f = Unsafe.class.getDeclaredField("theUnsafe"); + f.setAccessible(true); + Unsafe unsafe = (Unsafe) f.get(null); + + Unsafe API的大部分方法都是native实现,它由105个方法组成,主要包括以下几类: + (1)Info相关。主要返回某些低级别的内存信息:addressSize(), pageSize() + (2)Objects相关。主要提供Object和它的域操纵方法:allocateInstance(),objectFieldOffset() + (3)Class相关。主要提供Class和它的静态域操纵方法:staticFieldOffset(),defineClass(),defineAnonymousClass(),ensureClassInitialized() + (4)Arrays相关。数组操纵方法:arrayBaseOffset(),arrayIndexScale() + (5)Synchronization相关。主要提供低级别同步原语(如基于CPU的CAS(Compare-And-Swap)原语):monitorEnter(),tryMonitorEnter(),monitorExit(),compareAndSwapInt(),putOrderedInt() + (6)Memory相关。直接内存访问方法(绕过JVM堆直接操纵本地内存):allocateMemory(),copyMemory(),freeMemory(),getAddress(),getInt(),putInt() + + //扩充内存 + 2 public native long reallocateMemory(long address, long bytes); + 3 + 4 //分配内存 + 5 public native long allocateMemory(long bytes); + 6 + 7 //释放内存 + 8 public native void freeMemory(long address); + 9 + 10 //在给定的内存块中设置值 + 11 public native void setMemory(Object o, long offset, long bytes, byte value); + 12 + 13 //从一个内存块拷贝到另一个内存块 + 14 public native void copyMemory(Object srcBase, long srcOffset, Object destBase, long destOffset, long bytes); + 15 + 16 //获取值,不管java的访问限制,其他有类似的getInt,getDouble,getLong,getChar等等 + 17 public native Object getObject(Object o, long offset); + 18 + 19 //设置值,不管java的访问限制,其他有类似的putInt,putDouble,putLong,putChar等等 + 20 public native void putObject(Object o, long offset); + 21 + 22 //从一个给定的内存地址获取本地指针,如果不是allocateMemory方法的,结果将不确定 + 23 public native long getAddress(long address); + 24 + 25 //存储一个本地指针到一个给定的内存地址,如果地址不是allocateMemory方法的,结果将不确定 + 26 public native void putAddress(long address, long x); + 27 + 28 //该方法返回给定field的内存地址偏移量,这个值对于给定的filed是唯一的且是固定不变的 + 29 public native long staticFieldOffset(Field f); + 30 + 31 //报告一个给定的字段的位置,不管这个字段是private,public还是保护类型,和staticFieldBase结合使用 + 32 public native long objectFieldOffset(Field f); + 33 + 34 //获取一个给定字段的位置 + 35 public native Object staticFieldBase(Field f); + 36 + 37 //确保给定class被初始化,这往往需要结合基类的静态域(field) + 38 public native void ensureClassInitialized(Class c); + 39 + 40 //可以获取数组第一个元素的偏移地址 + 41 public native int arrayBaseOffset(Class arrayClass); + 42 + 43 //可以获取数组的转换因子,也就是数组中元素的增量地址。将arrayBaseOffset与arrayIndexScale配合使用, 可以定位数组中每个元素在内存中的位置 + 44 public native int arrayIndexScale(Class arrayClass); + 45 + 46 //获取本机内存的页数,这个值永远都是2的幂次方 + 47 public native int pageSize(); + 48 + 49 //告诉虚拟机定义了一个没有安全检查的类,默认情况下这个类加载器和保护域来着调用者类 + 50 public native Class defineClass(String name, byte[] b, int off, int len, ClassLoader loader, ProtectionDomain protectionDomain); + 51 + 52 //定义一个类,但是不让它知道类加载器和系统字典 + 53 public native Class defineAnonymousClass(Class hostClass, byte[] data, Object[] cpPatches); + 54 + 55 //锁定对象,必须是没有被锁的 + 56 public native void monitorEnter(Object o); + 57 + 58 //解锁对象 + 59 public native void monitorExit(Object o); + 60 + 61 //试图锁定对象,返回true或false是否锁定成功,如果锁定,必须用monitorExit解锁 + 62 public native boolean tryMonitorEnter(Object o); + 63 + 64 //引发异常,没有通知 + 65 public native void throwException(Throwable ee); + 66 + 67 //CAS,如果对象偏移量上的值=期待值,更新为x,返回true.否则false.类似的有compareAndSwapInt,compareAndSwapLong,compareAndSwapBoolean,compareAndSwapChar等等。 + 68 public final native boolean compareAndSwapObject(Object o, long offset, Object expected, Object x); + 69 + 70 // 该方法获取对象中offset偏移地址对应的整型field的值,支持volatile load语义。类似的方法有getIntVolatile,getBooleanVolatile等等 + 71 public native Object getObjectVolatile(Object o, long offset); + 72 + 73 //线程调用该方法,线程将一直阻塞直到超时,或者是中断条件出现。 + 74 public native void park(boolean isAbsolute, long time); + 75 + 76 //终止挂起的线程,恢复正常.java.util.concurrent包中挂起操作都是在LockSupport类实现的,也正是使用这两个方法 + 77 public native void unpark(Object thread); + 78 + 79 //获取系统在不同时间系统的负载情况 + 80 public native int getLoadAverage(double[] loadavg, int nelems); + 81 + 82 //创建一个类的实例,不需要调用它的构造函数、初使化代码、各种JVM安全检查以及其它的一些底层的东西。即使构造函数是私有,我们也可以通过这个方法创建它的实例,对于单例模式,简直是噩梦,哈哈 + 83 public native Object allocateInstance(Class cls) throws InstantiationException; + +``` \ No newline at end of file -- Gitee From b1800d27970228ae7c69efce2d015a196f8bc367 Mon Sep 17 00:00:00 2001 From: jiangyu_1013 <283810473@qq.com> Date: Sun, 15 Mar 2020 16:50:21 +0800 Subject: [PATCH 2/3] =?UTF-8?q?=E6=BA=90=E7=A0=81=E7=AC=94=E8=AE=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- second/week_02/36/AtomicStampedReference.md | 113 ++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 second/week_02/36/AtomicStampedReference.md diff --git a/second/week_02/36/AtomicStampedReference.md b/second/week_02/36/AtomicStampedReference.md new file mode 100644 index 0000000..aadebe9 --- /dev/null +++ b/second/week_02/36/AtomicStampedReference.md @@ -0,0 +1,113 @@ + +# AtomicStampedReference 解决ABA问题。 +##### ABA问题:发生在多线程中,一线程多次读取同一内存地址得到的值一样,它就认为该内存地址值“未修改过”。 +##### 实际可能为另一线程在这多次读取之间把该值改为了A,又改了B,最后再改回A。即:该值并不是“未修改过”。 + +# 1.继承关系: +``` java + public class AtomicInteger extends Number implements java.io.Serializable +``` + +# 2. 属性、内部类: +``` java + // 将元素值和版本号绑定在一起,存储在Pair的reference和stamp(邮票、戳的意思)中。 + private static class Pair { + final T reference; + final int stamp; + private Pair(T reference, int stamp) { + this.reference = reference; + this.stamp = stamp; + } + static Pair of(T reference, int stamp) { + return new Pair(reference, stamp); + } + } + + // 声明一个Pair类型的变量并使用Unsfae获取其偏移量,存储到pairOffset中。 + private volatile Pair pair; + private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe(); + private static final long pairOffset = objectFieldOffset(UNSAFE, "pair", AtomicStampedReference.class); +``` + +# 3.方法: + +``` java + // 参数:初始值、初始版本号。 + public AtomicStampedReference(V initialRef, int initialStamp) { + pair = Pair.of(initialRef, initialStamp); + } + + // 返回当前引用的值 + public V getReference() { + return pair.reference; + } + + // 返回戳记的当前值。 + public int getStamp() { + return pair.stamp; + } + + // 返回引用和戳记的当前值。 + public V get(int[] stampHolder) { + Pair pair = this.pair; + stampHolder[0] = pair.stamp; + return pair.reference; + } + + // 如果当前引用是预期引用且当前戳等于预期戳,则原子地将引用和戳的值设置为给定的更新值。 + public boolean weakCompareAndSet(V expectedReference, + V newReference, + int expectedStamp, + int newStamp) { + return compareAndSet(expectedReference, newReference, + expectedStamp, newStamp); + } + + 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) || + casPair(current, Pair.of(newReference, newStamp))); + } + + // 无条件地设置引用和戳记的值。参数:newReference引用的新值; newStamp邮票的新值。 + public void set(V newReference, int newStamp) { + Pair current = pair; + if (newReference != current.reference || newStamp != current.stamp) + this.pair = Pair.of(newReference, newStamp); + } + + // 如果当前引用是预期引用,则原子地将标记的值设置为给定的更新值。 + // 对该操作的任何给定调用都可能会失败,但当 当前值保持预期值,且没有其他线程也试图设置该值时,重复调用最终将成功。 + public boolean attemptStamp(V expectedReference, int newStamp) { + Pair current = pair; + return + expectedReference == current.reference && + (newStamp == current.stamp || + casPair(current, Pair.of(expectedReference, newStamp))); + } + + // Unsafe mechanics + private boolean casPair(Pair cmp, Pair val) { + return UNSAFE.compareAndSwapObject(this, pairOffset, cmp, val); + } + + static long objectFieldOffset(sun.misc.Unsafe UNSAFE, + String field, Class klazz) { + try { + return UNSAFE.objectFieldOffset(klazz.getDeclaredField(field)); + } catch (NoSuchFieldException e) { + // Convert Exception to corresponding Error + NoSuchFieldError error = new NoSuchFieldError(field); + error.initCause(e); + throw error; + } + } + +``` \ No newline at end of file -- Gitee From c3ac4691a7377218fde754ed017e1bb0c4f7511d Mon Sep 17 00:00:00 2001 From: jiangyu_1013 <283810473@qq.com> Date: Sun, 15 Mar 2020 17:43:24 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=E6=BA=90=E7=A0=81=E7=AC=94=E8=AE=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- second/week_02/36/LongAdder.md | 132 +++++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 second/week_02/36/LongAdder.md diff --git a/second/week_02/36/LongAdder.md b/second/week_02/36/LongAdder.md new file mode 100644 index 0000000..439754d --- /dev/null +++ b/second/week_02/36/LongAdder.md @@ -0,0 +1,132 @@ + +# LongAdder java8中新增的原子类。多线程时性能高于AtomicLong。 +##### + +# 1.继承关系: +``` java + public class LongAdder extends Striped64 implements Serializable +``` + +# 2. 属性、内部类: +``` java + // 将元素值和版本号绑定在一起,存储在Pair的reference和stamp(邮票、戳的意思)中。 +``` + +# 3.方法: + +``` java + // 加方法 + public void add(long x) { + Cell[] as; long b, v; int m; Cell a; + if ((as = cells) != null || !casBase(b = base, b + x)) { + boolean uncontended = true; + if (as == null || (m = as.length - 1) < 0 || + (a = as[getProbe() & m]) == null || + !(uncontended = a.cas(v = a.value, v + x))) + longAccumulate(x, null, uncontended); + } + } + + // 等价于add(1); + public void increment() { + add(1L); + } + + // 等价于add(-1); + public void decrement() { + add(-1L); + } + + // 返回当前和。返回的值是一个原子快照;在没有并发更新的情况下调用会返回一个准确的结果,但是在计算总和时发生的并发更新可能不会被合并。 + public long sum() { + Cell[] as = cells; Cell a; + long sum = base; + if (as != null) { + for (int i = 0; i < as.length; ++i) { + if ((a = as[i]) != null) + sum += a.value; + } + } + return sum; + } + + // 重置将总和保持为零的变量。 + // 此方法可能是创建新加法器的有用替代方法,但仅在没有并发更新时有效。因为这个方法本质上是racy,所以只有在知道没有线程并发更新时才应该使用它。 + public void reset() { + Cell[] as = cells; Cell a; + base = 0L; + if (as != null) { + for (int i = 0; i < as.length; ++i) { + if ((a = as[i]) != null) + a.value = 0L; + } + } + } + + // 求和后重置。此方法可应用于多线程计算之间的静止点。如果有与此方法并发的更新,则返回值保证是重置之前发生的最终值。 + public long sumThenReset() { + Cell[] as = cells; Cell a; + long sum = base; + base = 0L; + if (as != null) { + for (int i = 0; i < as.length; ++i) { + if ((a = as[i]) != null) { + sum += a.value; + a.value = 0L; + } + } + } + return sum; + } + + public String toString() { + return Long.toString(sum()); + } + + public long longValue() { + return sum(); + } + + public int intValue() { + return (int)sum(); + } + + public float floatValue() { + return (float)sum(); + } + + public double doubleValue() { + return (double)sum(); + } + + // 序列化代理 + private static class SerializationProxy implements Serializable { + private static final long serialVersionUID = 7249069246863182397L; + + // sum() 返回的当前值。 + private final long value; + + SerializationProxy(LongAdder a) { + value = a.sum(); + } + + // 返回一个初始状态由该代理持有的LongAdder对象。 + private Object readResolve() { + LongAdder a = new LongAdder(); + a.base = value; + return a; + } + } + + // 返回表示此实例状态的 SerializationProxy + private Object writeReplace() { + return new SerializationProxy(this); + } + + // 检查 + private void readObject(java.io.ObjectInputStream s) + throws java.io.InvalidObjectException { + throw new java.io.InvalidObjectException("Proxy required"); + } + +``` \ No newline at end of file -- Gitee