diff --git a/second/week_02/82/AtomicInteger.md b/second/week_02/82/AtomicInteger.md new file mode 100644 index 0000000000000000000000000000000000000000..8eaac25b0efffba79e58aa65f1508880a50c978e --- /dev/null +++ b/second/week_02/82/AtomicInteger.md @@ -0,0 +1,39 @@ +#### AtomicInteger 源码分析 + +自增自减操作 + +```java +// 获取自增 +public final int getAndIncrement() { + return unsafe.getAndAddInt(this, valueOffset, 1); +} +// 自增获取 +public final int incrementAndGet() { + return unsafe.getAndAddInt(this, valueOffset, 1) + 1; +} +// 获取自减 +public final int getAndDecrement() { + return unsafe.getAndAddInt(this, valueOffset, -1); +} +// 自减获取 +public final int decrementAndGet() { + return unsafe.getAndAddInt(this, valueOffset, -1) - 1; +} +``` + + + +cas自旋操作 + +```java +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/second/week_02/82/unsafe.md b/second/week_02/82/unsafe.md new file mode 100644 index 0000000000000000000000000000000000000000..b9465954b5ae3748e3290f0031f292a3bca21c32 --- /dev/null +++ b/second/week_02/82/unsafe.md @@ -0,0 +1,100 @@ +#### Unsafe 源码分析 + +数组 + +```java +// 返回数组中第一个元素的偏移地址 +public native int arrayBaseOffset(Class var1); +// 返回数组中一个元素占用的大小 +public native int arrayIndexScale(Class var1); + +// 1. first index 偏移量 +private static final int base = unsafe.arrayBaseOffset(int[].class); +// 2. scale = 4; +int scale = unsafe.arrayIndexScale(int[].class); +// 3. 计算 scale 二进制后面有几个0,如scale = 4(0100),shift = 2 +shift = 31 - Integer.numberOfLeadingZeros(scale); +// 4. 根据index对scale进行乘法运算获取偏移量 offset,如index = 1,offset = 4(1 << 2) + 16 = 20 +offset = index << shift + base; +// 5. 通过 offset 原子的获取对应的值 +unsafe.getIntVolatile(array, offset); +``` + + + +多线程 + +```java +// 获取系统指针的大小, 64 位是8 +public native int addressSize(); +// 获取内存页大小,2的幂次方,我本机测试是4096 +public native int pageSize(); + +// 取消阻塞 +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); +``` + +内存 + +```java +// 内存分配,相当于c++的os::malloc +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 native void copyMemory(Object var1, long var2, Object var4, long var5, long var7); +// 释放内存,相当于c++的os::free +public native void freeMemory(long var1); +// 获取给定地址的XX类型的值 +public native byte getXx(long var1); +// 为给定地址设置XX类型的值 +public native void putXx(long var1, xx var3); +``` + +cas + +// 根据第二个参数”偏移量”去拿偏移量这么多的属性的值和第三个参数对比,如果相同则将该属性值替换为第四个参数。该偏移量是指某个字段相对Java对象的起始位置的偏移量,可以通过unsafe.objectFieldOffset(param)去获取对应属性的偏移量。 + +```java +`public final native boolean compareAndSwapXx(Object var1, long var2, Xx var4, Xx var5); +``` + +类-class&object + +```java + +// 获取静态字段的内存地址偏移量 +public native long staticFieldOffset(Field var1); +// 获取一个静态类中给定字段的对象指针 +public native Object staticFieldBase(Field var1); +// 判断是否需要初始化一个类,因为有可能类还没初始化却去获取静态属性 +public native boolean shouldBeInitialized(Class var1); +// 检测类是否已经初始化 +public native void ensureClassInitialized(Class var1); +// 定义一个类 +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 long objectFieldOffset(Field var1); +// 从对象的指定偏移量处获取变量的引用,使用volatile的加载语义 +public native Object getObjectVolatile(Object o, long offset); +// 存储变量的引用到对象的指定的偏移量处,使用volatile的存储语义 +public native void putObjectVolatile(Object o, long offset, Object x); +// 有序、延迟版本的putObjectVolatile方法,不保证值的改变被其他线程立即看到。只有在field被volatile修饰符修饰时有效 +public native void putOrderedObject(Object o, long offset, Object x); +// 绕过构造方法、初始化代码来创建对象 +public native Object allocateInstance(Class cls) throws InstantiationException; +``` + diff --git "a/second/week_03/82/AQS\345\210\206\346\236\220.md" "b/second/week_03/82/AQS\345\210\206\346\236\220.md" new file mode 100644 index 0000000000000000000000000000000000000000..8dea3c341feb6203f8a75c22af5885609e0c95a3 --- /dev/null +++ "b/second/week_03/82/AQS\345\210\206\346\236\220.md" @@ -0,0 +1,50 @@ +AQS是AbstractQueuedSynchronizer的简称。AQS提供了一种实现阻塞锁和一系列依赖FIFO(先进先出)等待队列的同步器的框架,如下图所示。 + +![img](C:\Users\itliusir\AppData\Local\YNote\data\amaze0618@gmail.com\bc1f4e5662a9490fb396ce55f51561e3\clipboard.png) + +1,**synchronized**内部锁升级过程 (适合并发量很大的情况) + +```java +public class AQSTest2 { + public static int m=0; + + public static void main(String[] args)throws Exception{ + Thread[] threads =new Thread[100]; + for (int i = 0; i < threads.length; i++) { + threads[i]=new Thread(()->{ + synchronized (AQSTest2.class){ + for (int j = 1; j <=100 ; j++){ + m++;try { +// Thread.sleep(500); + }catch (Exception e){ + e.printStackTrace(); + } + System.out.println(j+":"+m); + } + } + }); +// Thread.sleep(500); +// System.out.println(i+":"+m); + } + for (Thread t: threads) t.start(); + for (Thread t: threads) t.join(); + System.out.println(m); + } +} +``` + + + +- 系统级别的锁,交互较慢 +- jdk1.5后优化:(优先)偏向锁 --(被其他线程征用时)-> 轻量级锁(自旋锁spinlock,自旋10次还没拿到)--->重量级锁 +- 1.5之后增加concurrent多种锁类型,jvm内部实现锁,效率提高 + +2,可重入锁 + +3,CAS无锁算法--compareAndSwap (要看cpu是否支持)--乐观锁 + +(适合并发量不大的情况,一直自旋会占用cpu) + +if(compareAndSetState(0,1)){ return unsafe.compareAndSwapInt(this, valueOffset, expect, update); } + +循环判断状态值为0时才改变1,必须保证原子性操作 \ No newline at end of file diff --git "a/second/week_03/82/volatile\345\210\206\346\236\220.md" "b/second/week_03/82/volatile\345\210\206\346\236\220.md" new file mode 100644 index 0000000000000000000000000000000000000000..3bafdda5e48b68b3e50d6247d7af0c2cb1cdebd7 --- /dev/null +++ "b/second/week_03/82/volatile\345\210\206\346\236\220.md" @@ -0,0 +1,73 @@ +## volatile修饰的变量 + +​ 关键字volatile是java虚拟机中提供的最轻量级的同步机制。java内存模型对volatile专门定义了一些特殊的访问规则如下: + + +- **只有当线程T对变量V执行的前一个动作是load,线程T对变量V才能执行use动作;同时只有当线程T对变量V执行的后一个动作是use的时候线程T对变量V才能执行load操作。**所以,线程T对变量V的use动作和线程T对 +- **只有当线程T对变量V执行的前一个动作是assign的时候,线程T对变量V才能执行store动作;同时只有当线程T对变量V执行的后一个动作是store的时候,线程T对变量V才能执行assign动作。**所以,线程T对变量V的 +- **也就是说在同一个线程内部,被volatile修饰的变量不会被指令重排序,保证代码的执行顺序和程序的顺序相同。** + +概括:**volatile类型的变量保证对所有线程的可见性**。 + +​ **volatile类型的变量禁止指令重排序优化**。 + +​ **valatile类型的变量保证对所有线程的可见性** + +摘抄案例: + +```csharp +public class VolatileTest { + public static volatile int race = 0; + public static void increase() { + race++ + } + + private static final int THREADS_COUNT = 20; + + public void static main(String[] args) { + Thread[] threads = new Thread[THREADS_COUNT); + for (int = 0; i < THREADS_COUNT; i++) { + threads[i] = new Thread(new Runnable(){ + @Override + public void run() { + for (int j = 0; j < 10000; j++) { + increase(); + } + } + }); + threads[i].start(); + } + while (Thread.activeCount() > 1) { + Thread.yield(); + } + System.out.println(race); + } +} +``` + +代码就是对volatile类型的变量启动了20个线程,每个线程对变量执行1w次加1操作,如果volatile变量并发操作没有问题的话,那么结果应该是输出20w,但是结果运行的时候每次都是小于20w,这就是因为`race++`操作不是原子性的,是分多个步骤完成的。假设两个线程a、b同时取到了主内存的值,是0,这是没有问题的,在进行`++`操作的时候假设线程a执行到一半,线程b执行完了,这时线程b立即同步给了主内存,主内存的值为1,而线程a此时也执行完了,同步给了主内存,此时的值仍然是1,线程b的结果被覆盖掉了。 + +- **volatile变量禁止指令重排序优化** + +```java +Map configOptions; +char[] configText; +//volatile类型bianliang +volatile boolean initialized = false; + +//假设以下代码在线程A中执行 +//模拟读取配置信息,读取完成后认为是初始化完成 +configOptions = new HashMap(); +configText = readConfigFile(fileName); +processConfigOptions(configText, configOptions); +initialized = true; + +//假设以下代码在线程B中执行 +//等待initialized为true后,读取配置信息进行操作 +while ( !initialized) { + sleep(); +} +doSomethingWithConfig(); +``` + +如果initialiezd是普通变量,没有被volatile修饰,那么线程A执行的代码的修改初始化完成的结果`initialized = true`就有可能先于之前的三行代码执行,而此时线程B发现initialized为true了,就执行`doSomethingWithConfig()`方法,但是里面的配置信息都是null的,就会出现问题了。