diff --git "a/week_02/62/AtomicInteger\346\272\220\347\240\201\345\210\206\346\236\220.md" "b/week_02/62/AtomicInteger\346\272\220\347\240\201\345\210\206\346\236\220.md" new file mode 100644 index 0000000000000000000000000000000000000000..0bb674450621b0e12600f5299ff2bbc16cc0c18a --- /dev/null +++ "b/week_02/62/AtomicInteger\346\272\220\347\240\201\345\210\206\346\236\220.md" @@ -0,0 +1,29 @@ +# AtomicInteger + + 之前已经看过这个类源码,不过这次有更加深刻的认识。 + private static final Unsafe unsafe = Unsafe.getUnsafe(); //获取unsafe,可见rt文件下的类都是由引进到类加载 + private static final long valueOffset; + + static { + try { + valueOffset = unsafe.objectFieldOffset + (AtomicInteger.class.getDeclaredField("value")); //首先获取偏移量,方便后续使用unsafe的cas + } catch (Exception ex) { throw new Error(ex); } + } + + private volatile int value; //volative 保证可见性,一致性,但是不能保证原子性,在java中除了基础数据类型的原子操作是 + 原子类型的,long ,double的操作都不是原子类型的。一般都不是原子操作。无法保证操作原子性 + + + public final int decrementAndGet() { + return unsafe.getAndAddInt(this, valueOffset, -1) - 1; //底层实现还是使用 unsafe + } + + 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; + } diff --git "a/week_02/62/LongAdder\346\272\220\347\240\201\345\210\206\346\236\220.md" "b/week_02/62/LongAdder\346\272\220\347\240\201\345\210\206\346\236\220.md" new file mode 100644 index 0000000000000000000000000000000000000000..c58e3bab063b76994187f923b9403bbc62f4b3db --- /dev/null +++ "b/week_02/62/LongAdder\346\272\220\347\240\201\345\210\206\346\236\220.md" @@ -0,0 +1,107 @@ +# LongAdder + +##源码分析 : + /** + * Table of cells. When non-null, size is a power of 2. + */ + transient volatile Cell[] cells; + + +public void add(long x) { + Cell[] as; long b, v; int m; Cell a; //as多个槽 ,感觉有点象 1.8里面的concurrentMap + 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))) //实际调用还是 cas + longAccumulate(x, null, uncontended); + } + } + + 与AtomicLong区别是,一个是针对一个值进行自旋,一个是对多个槽进行自旋,减少并发竞争,以空间换时间。 + + 引用 + final void longAccumulate(long x, LongBinaryOperator fn, + boolean wasUncontended) { + int h; + // 这个步骤是在初始化当前线程的变量threadLocalRandomProbe, + // 如果不了解 ThreadLocalRandom,可以把它暂时看成一个线程安全的随机数种子, + // 以后有篇幅在单独介绍ThreadLocalRandom + if ((h = getProbe()) == 0) { + ThreadLocalRandom.current(); // force initialization + h = getProbe(); + wasUncontended = true; + } + boolean collide = false; // True if last slot nonempty + done: for (;;) { + Cell[] cs; Cell c; int n; long v; + if ((cs = cells) != null && (n = cs.length) > 0) { + if ((c = cs[(n - 1) & h]) == null) { + if (cellsBusy == 0) { // Try to attach new Cell + Cell r = new Cell(x); // Optimistically create + if (cellsBusy == 0 && casCellsBusy()) { + try { // Recheck under lock + Cell[] rs; int m, j; + if ((rs = cells) != null && + (m = rs.length) > 0 && + rs[j = (m - 1) & h] == null) { + rs[j] = r; + break done; + } + } finally { + cellsBusy = 0; + } + continue; // Slot is now non-empty + } + } + collide = false; + } + else if (!wasUncontended) // CAS already known to fail + wasUncontended = true; // Continue after rehash + // 这里判断如果cell存在就对当前元素执行CAS操作 + else if (c.cas(v = c.value, + (fn == null) ? v + x : fn.applyAsLong(v, x))) + break; + // NCPU 是我们之前提到的CPU核心数量。 + // 在扩容cells之前他会检查当前数组个数是否超过CPU核心数量, + // 因为他认为如果扩容超过CPU核心数量,只会造成更多的CAS失败是没有意义的。 + // 当然这个扩容的操作仍然满足2的幂次方。 + // n >= NCPU 是不会再进行扩容了。 + else if (n >= NCPU || cells != cs) + collide = false; // At max size or stale + else if (!collide) + collide = true; + // 这里我们可以看出扩容要满足两个条件, + // 1个是对NCPU 的数量对比,第2个是当前元素操作有冲突, + // 然后采用casCellsBusy方法来加锁。 + else if (cellsBusy == 0 && casCellsBusy()) { + try { + if (cells == cs) // Expand table unless stale + cells = Arrays.copyOf(cs, n << 1); + } finally { + cellsBusy = 0; + } + collide = false; + continue; // Retry with expanded table + } + h = advanceProbe(h); + } + // 这里是初始化cells 数组 + else if (cellsBusy == 0 && cells == cs && casCellsBusy()) { + try { // Initialize table + if (cells == cs) { + Cell[] rs = new Cell[2]; + rs[h & 1] = new Cell(x); + cells = rs; + break done; + } + } finally { + cellsBusy = 0; + } + } + // Fall back on using base + else if (casBase(v = base, + (fn == null) ? v + x : fn.applyAsLong(v, x))) + break done; + } + } \ No newline at end of file diff --git "a/week_02/62/UnSafe \346\272\220\347\240\201\345\210\206\346\236\220.md" "b/week_02/62/UnSafe \346\272\220\347\240\201\345\210\206\346\236\220.md" new file mode 100644 index 0000000000000000000000000000000000000000..196386431d17f74b303c50c1ce8e829521c8d192 --- /dev/null +++ "b/week_02/62/UnSafe \346\272\220\347\240\201\345\210\206\346\236\220.md" @@ -0,0 +1,66 @@ +# Unsafe 源码分析 + + + +## 继承体系 + +私有类,且构造器私有 + +## 源码分析 + //构造方法私有 + private Unsafe() { + } + + @CallerSensitive + public static Unsafe getUnsafe() { + Class var0 = Reflection.getCallerClass(); + if (!VM.isSystemDomainLoader(var0.getClassLoader())) { // 判断调用此方法的类的加载器是否是有 bootstrap加载器加载, + 如果不是则直接报错,boortrap > root > app + throw new SecurityException("Unsafe"); + } else { + return theUnsafe; + } + } + + 所以获取的方式是 1、 Unsafe.clsaa.getDeclaredField("theunsafe").setAccessible(true).get(null); + 2、修改jvm bootsrap参数 java -Xbootclasspath:/usr/jdk1.7.0/jre/lib/rt.jar:. com.mishadoff.magic.UnsafeClient + + 偏移量的理解: + JMM中对象堆栈分配,在堆中对象分为两种方法,指针和句柄,这里面的深层次内容,还没有完全掌握。 + 对象头在64位系统中,是12位,所以在调用geiInt,过着其他方法的时候,通过设置参数为12,就可以获取紧跟着的一个int参数值, + 也可以使用set*方法对内存直接赋值。 + + 直接内存操作 + // 分配内存 + public native long allocateMemory(long var1); + // 重新分配内存 + public native long reallocateMemory(long var1, long var3); + // 内存初始化 + public native void setMemory(long var1, long var3, byte var5); + // 内存复制 + public native void copyMemory(Object var1, long var2, Object var4, long var5, long var7); + // 清除内存 + public native void freeMemory(long var1); + + 内存屏障 + public native void loadFence(); + loadFence:保证在这个屏障之前的所有读操作都已经完成。 + public native void storeFence(); + storeFence:保证在这个屏障之前的所有写操作都已经完成。 + public native void fullFence(); + fullFence:保证在这个屏障之前的所有读写操作都已经完成。 + + 线程调度: + // 挂起线程 + public static void park(Object blocker) { + Thread t = Thread.currentThread(); + setBlocker(t, blocker); // 通过Unsafe的putObject方法设置阻塞阻塞当前线程的blocker + UNSAFE.park(false, 0L); // 通过Unsafe的park方法来阻塞当前线程,注意此方法将当前线程阻塞后,当前线程就不会继续往下走了,直到其他线程unpark此线程 + setBlocker(t, null); // 清除blocker + } + + // 唤醒线程 + public static void unpark(Thread thread) { + if (thread != null) + UNSAFE.unpark(thread); + } \ No newline at end of file