From 6fd00116aa23a6df012f0808e93b030226921d3a Mon Sep 17 00:00:00 2001 From: wxm <602260235@qq.com> Date: Sun, 15 Mar 2020 17:19:59 +0800 Subject: [PATCH] =?UTF-8?q?=E7=AC=AC=E4=BA=8C=E5=91=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...20\347\240\201\345\210\206\346\236\220.md" | 96 ++++++++ ...20\347\240\201\345\210\206\346\236\220.md" | 92 ++++++++ second/week_02/73/Demo.java | 63 ++++++ ...20\347\240\201\345\210\206\346\236\220.md" | 209 ++++++++++++++++++ 4 files changed, 460 insertions(+) create mode 100644 "second/week_02/73/AtomicInteger\346\272\220\347\240\201\345\210\206\346\236\220.md" create mode 100644 "second/week_02/73/AtomicStampedReference\346\272\220\347\240\201\345\210\206\346\236\220.md" create mode 100644 second/week_02/73/Demo.java create mode 100644 "second/week_02/73/Unsafe\346\272\220\347\240\201\345\210\206\346\236\220.md" diff --git "a/second/week_02/73/AtomicInteger\346\272\220\347\240\201\345\210\206\346\236\220.md" "b/second/week_02/73/AtomicInteger\346\272\220\347\240\201\345\210\206\346\236\220.md" new file mode 100644 index 0000000..e5514b6 --- /dev/null +++ "b/second/week_02/73/AtomicInteger\346\272\220\347\240\201\345\210\206\346\236\220.md" @@ -0,0 +1,96 @@ +## 关于AtomicInteger的随笔 + + +### 属性 + +```java +// 获取Unsafe实例 +private static final Unsafe unsafe = Unsafe.getUnsafe(); +// 标识value字段的偏移量,用来寻找 +private static final long valueOffset; +// 静态代码块,通过unsafe获取value的偏移量 +static { + try { + valueOffset = unsafe.objectFieldOffset + (AtomicInteger.class.getDeclaredField("value")); + } catch (Exception ex) { throw new Error(ex); } +} + +// 存储int类型值的地方,使用volatile修饰 +private volatile int value; +``` + +(1)使用int类型的value存储值,且使用volatile修饰,volatile主要是保证可见性,即一个线程修改对另一个线程立即可见,主要的实现原理是内存屏障,这里不展开来讲,有兴趣的可以自行查阅相关资料。 + +(2)调用Unsafe的objectFieldOffset()方法获取value字段在类中的偏移量,用于后面CAS操作时使用。 + +### 方法 + +#### 构造方法和没有使用cas的get和set方法 + +```java + public AtomicInteger(int initialValue) { + value = initialValue; + } + + public AtomicInteger() { + } + + public final int get() { + return value; + } + + public final void set(int newValue) { + value = newValue; + } +``` + +#### 通过Unsafe实现的set方法 + +```java + // 调用putOrderedInt方法,有序写入,不保证原子性 + public final void lazySet(int newValue) { + unsafe.putOrderedInt(this, valueOffset, newValue); + } + // 最终通过CAS方法赋值 + public final int getAndSet(int newValue) { + return unsafe.getAndSetInt(this, valueOffset, newValue); + } + // 直接调用 + 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); + } +``` + +#### 自增自减以及加减的原子操作方法 + +```java + // 自增1,调用unsafe的getAndAddInt方法,而unsafe中这个方法是调用CAS方法完成。返回未增加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,返回加1后的值 + public final int incrementAndGet() { + return unsafe.getAndAddInt(this, valueOffset, 1) + 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; + } +``` diff --git "a/second/week_02/73/AtomicStampedReference\346\272\220\347\240\201\345\210\206\346\236\220.md" "b/second/week_02/73/AtomicStampedReference\346\272\220\347\240\201\345\210\206\346\236\220.md" new file mode 100644 index 0000000..cf26a79 --- /dev/null +++ "b/second/week_02/73/AtomicStampedReference\346\272\220\347\240\201\345\210\206\346\236\220.md" @@ -0,0 +1,92 @@ +## 关于AtomicStampedReference的随笔 + + +### 属性 + +```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类型变量,在上面已经定义 + private volatile Pair pair; + // 获取Unsafe实例 + private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe(); + // 获取pair变量的偏移量,方便寻址 + private static final long pairOffset = + objectFieldOffset(UNSAFE, "pair", AtomicStampedReference.class); +``` + +### 方法 + +#### 构造方法 + +```java + public AtomicStampedReference(V initialRef, int initialStamp) { + pair = Pair.of(initialRef, initialStamp); + } +``` + +构造方法只有一个,需要传入两个参数,一个是初始值,一个是初始版本号。 + +#### 普通get/set方法 + +```java + // 返回值 + 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 void set(V newReference, int newStamp) { + Pair current = pair; + if (newReference != current.reference || newStamp != current.stamp) + this.pair = Pair.of(newReference, newStamp); + } +``` + +#### compareAndSet()方法 + +```java + public boolean compareAndSet(V expectedReference, + V newReference, + int expectedStamp, + int newStamp) { + // 获取当前pair + Pair current = pair; + return + // 判断当前reference有没有变化 + expectedReference == current.reference && + // 判断版本号有没有变化 + expectedStamp == current.stamp && + // 判断要更改的reference和版本号是否与当前的pair一致,是则不用更改,否则更新 + ((newReference == current.reference && + newStamp == current.stamp) || + casPair(current, Pair.of(newReference, newStamp))); + } + + private boolean casPair(Pair cmp, Pair val) { + return UNSAFE.compareAndSwapObject(this, pairOffset, cmp, val); + } +``` + +调用了Unsafe类中的CAS方法,实现了原子操作,并通过版本号的引入克服了ABA问题。 + + diff --git a/second/week_02/73/Demo.java b/second/week_02/73/Demo.java new file mode 100644 index 0000000..6985756 --- /dev/null +++ b/second/week_02/73/Demo.java @@ -0,0 +1,63 @@ +package com.nupoo.cloud.schedule; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.atomic.AtomicStampedReference; + +/** + * @author wxm + * @date 2020-03-15 + */ +public class Demo { + static AtomicReference atomicReference = new AtomicReference<>(100); + static AtomicStampedReference atomicStampedReference = new AtomicStampedReference(100, 1); + + public static void main(String[] args) { +// new Thread(() -> { +// atomicReference.compareAndSet(100, 101); +// atomicReference.compareAndSet(101, 100); +// }, "t1").start(); +// new Thread(() -> { +// try { +// TimeUnit.SECONDS.sleep(1); +// } catch (InterruptedException e) { +// e.printStackTrace(); +// } +// System.out.println(atomicReference.compareAndSet(100, 2019) + " " + atomicReference.get()); +// }, "t2").start(); + + new Thread(() -> { + int stamp = atomicStampedReference.getStamp(); + System.out.println(Thread.currentThread().getName() + " 第1次版本号: " + stamp); + + atomicStampedReference.compareAndSet(100, 101, + atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1); + System.out.println(Thread.currentThread().getName() + " 第2次版本号: " + atomicStampedReference.getStamp()); + + atomicStampedReference.compareAndSet(101, 100, + atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1); + System.out.println(Thread.currentThread().getName() + " 第3次版本号: " + atomicStampedReference.getStamp()); + + }, "t3").start(); + + + new Thread(() -> { + int stamp = atomicStampedReference.getStamp(); + System.out.println(Thread.currentThread().getName() + " 第1次版本号: " + stamp); + + try { + TimeUnit.SECONDS.sleep(4); + } catch (InterruptedException e) { + e.printStackTrace(); + } + boolean b = atomicStampedReference.compareAndSet(100, 2019, + stamp, stamp + 1); + System.out.println(Thread.currentThread().getName() + " 修改是否成功: " + b + " 当前版本 :" + atomicStampedReference.getStamp()); + + System.out.println(Thread.currentThread().getName() + " 当前实际值: " + atomicStampedReference.getReference()); + + }, "t4").start(); + + + } +} \ No newline at end of file diff --git "a/second/week_02/73/Unsafe\346\272\220\347\240\201\345\210\206\346\236\220.md" "b/second/week_02/73/Unsafe\346\272\220\347\240\201\345\210\206\346\236\220.md" new file mode 100644 index 0000000..c4e7844 --- /dev/null +++ "b/second/week_02/73/Unsafe\346\272\220\347\240\201\345\210\206\346\236\220.md" @@ -0,0 +1,209 @@ +##关于Unsafe的随笔 + +> Unsafe为我们提供了访问底层的机制,这种机制仅供java核心类库使用,而不应该被普通用户使用。 + +> java是没有指针的,默认是由JVM进行内存的分配与垃圾回收,这时候就需要Unsafe类。UnSafe提供了硬件级别的原子操作,Unsafe类通过JNI的方式访问本地的C++实现库从而使java具有了直接操作内存空间的能力,但这同时也带来了一定的问题,如果不合理地使用Unsafe类操作内存空间,可能导致内存泄漏 + + +##1 获取Unsafe +```Java +@CallerSensitive +public static Unsafe getUnsafe() { + Class caller = Reflection.getCallerClass(); + if (!VM.isSystemDomainLoader(caller.getClassLoader())) + throw new SecurityException("Unsafe"); + return theUnsafe; +} +``` +#### Unsafe提供了一个静态方法getUnsafe()用于获取它的实例对象,但直接调用会抛出SecurityException异常,这是因为Unsafe只提供给Java核心类使用,那么可以通过反射获取它的实例: +```Java +public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException{ + Field field = Unsafe.class.getDeclaredField("theUnsafe");//getField只能获取public属性 + field.setAccessible(true);//关闭访问安全检查开关 + Unsafe unsafe = (Unsafe) field.get(null); + } +``` + +##2 使用Unsafe实例化一个类 +```java + //Unsafe通过一个native方法实现实例化一个类, 但是只会分配内存, 并不会调用该类的构造方法 + public native Object allocateInstance(Class var1) throws InstantiationException; +``` + +##3 修改私有字段的值 +使用Unsafe的putXXX()方法,我们可以修改任意私有字段的值。 +```java +public class UnsafeTest { + public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException { + //获取unsafe实例 + Field f = Unsafe.class.getDeclaredField("theUnsafe"); + f.setAccessible(true); + Unsafe unsafe = (Unsafe) f.get(null); + //获取user实例, 并指定age = 19 + User user = new User(19); + System.out.println(user.getAge());//19 + //通过反射获取User.class的age属性 + Field age = user.getClass().getDeclaredField("age"); + //调用unsafe.putInt, 参数为: 指定对象, 偏移量, 修改后的值 + //putInt方法通过获取user对象的起始地址, 加上objectFieldOffset()获取到的偏移值得到对应的update地址, 然后将20写入对应的update地址 + unsafe.putInt(user, unsafe.objectFieldOffset(age), 20); + System.out.println(user.getAge());//20 + } +} + +class User { + private int age; + + public User(int age) { + this.age = age; + } + + public int getAge() { + return age; + } +} +``` + +##4 Unsafe类 + + ```Java +//下面是sun.misc.Unsafe.java类源码 +package sun.misc; +import java.lang.reflect.Field; +/*** + * 这个类提供了一个更底层的操作并且应该在受信任的代码中使用。可以通过内存地址 + * 存取fields,如果给出的内存地址是无效的那么会有一个不确定的运行表现。 + */ +public class Unsafe +{ + private static Unsafe unsafe = new Unsafe(); +//使用私有默认构造器防止创建多个实例 + private Unsafe() + { + } + /*** + * 获取Unsafe的单例,这个方法调用应该防止在不可信的代码中实例, + * 因为unsafe类提供了一个低级别的操作,例如直接内存存取。 + * 如果安全管理器不存在或者禁止访问系统属性 + */ + public static Unsafe getUnsafe() + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkPropertiesAccess(); + return unsafe; + } + + /*** + * 返回指定静态field的内存地址偏移量,在这个类的其他方法中这个值只是被用作一个访问 + * 特定field的一个方式。这个值对于 给定的field是唯一的,并且后续对该方法的调用都应该 + * 返回相同的值。 + */ + public native long objectFieldOffset(Field field); + /*** + * 在obj的offset位置比较integer field和期望的值,如果相同则更新。这个方法 + * 的操作应该是原子的,因此提供了一种不可中断的方式更新integer field。 + */ + public native boolean compareAndSwapInt(Object obj, long offset, + int expect, int update); + /*** + * 在obj的offset位置比较long field和期望的值,如果相同则更新。这个方法 + * 的操作应该是原子的,因此提供了一种不可中断的方式更新long field。 + */ + public native boolean compareAndSwapLong(Object obj, long offset, + long expect, long update); + /*** + * 在obj的offset位置比较object field和期望的值,如果相同则更新。这个方法 + * 的操作应该是原子的,因此提供了一种不可中断的方式更新object field。 + */ + public native boolean compareAndSwapObject(Object obj, long offset, + Object expect, Object update); + /*** + * 设置obj对象中offset偏移地址对应的整型field的值为指定值。这是一个有序或者 + * 有延迟的putIntVolatile方法,并且不保证值的改变被其他线程立 + * 即看到。只有在field被volatile修饰并且期望被意外修改的时候 + * 使用才有用。 + */ + public native void putOrderedInt(Object obj, long offset, int value); + /*** + * 设置obj对象中offset偏移地址对应的long型field的值为指定值。这是一个有序或者 + * 有延迟的putLongVolatile方法,并且不保证值的改变被其他线程立 + * 即看到。只有在field被volatile修饰并且期望被意外修改的时候 + * 使用才有用。 + */ + public native void putOrderedLong(Object obj, long offset, long value); + /*** + * 设置obj对象中offset偏移地址对应的object型field的值为指定值。这是一个有序或者 + * 有延迟的putObjectVolatile方法,并且不保证值的改变被其他线程立 + * 即看到。只有在field被volatile修饰并且期望被意外修改的时候 + * 使用才有用。 + */ + public native void putOrderedObject(Object obj, long offset, Object value); + /*** + * 设置obj对象中offset偏移地址对应的整型field的值为指定值。支持volatile store语义 + */ + public native void putIntVolatile(Object obj, long offset, int value); + /*** + * 获取obj对象中offset偏移地址对应的整型field的值,支持volatile load语义。 + */ + public native int getIntVolatile(Object obj, long offset); + /*** + * 设置obj对象中offset偏移地址对应的long型field的值为指定值。支持volatile store语义 + */ + public native void putLongVolatile(Object obj, long offset, long value); + /*** + * 设置obj对象中offset偏移地址对应的long型field的值为指定值。 + */ + public native void putLong(Object obj, long offset, long value); + /*** + * 获取obj对象中offset偏移地址对应的long型field的值,支持volatile load语义。 + */ + public native long getLongVolatile(Object obj, long offset); + /*** + * 获取obj对象中offset偏移地址对应的long型field的值 + */ + public native long getLong(Object obj, long offset); + /*** + * 设置obj对象中offset偏移地址对应的object型field的值为指定值。支持volatile store语义 + */ + public native void putObjectVolatile(Object obj, long offset, Object value); + /*** + * 设置obj对象中offset偏移地址对应的object型field的值为指定值。 + */ + public native void putObject(Object obj, long offset, Object value); + /*** + * 获取obj对象中offset偏移地址对应的object型field的值,支持volatile load语义。 + */ + public native Object getObjectVolatile(Object obj, long offset); + /*** + * 获取给定数组中第一个元素的偏移地址。 + * 为了存取数组中的元素,这个偏移地址与arrayIndexScale + * 方法的非0返回值一起被使用。 + */ + public native int arrayBaseOffset(Class arrayClass); + /*** + * 获取用户给定数组寻址的换算因子.一个合适的换算因子不能返回的时候(例如:基本类型), + * 返回0.这个返回值能够与arrayBaseOffset + * 一起使用去存取这个数组class中的元素 + */ + public native int arrayIndexScale(Class arrayClass); + + /*** + * 释放被park创建的在一个线程上的阻塞.这个 + * 方法也可以被使用来终止一个先前调用park导致的阻塞. + * 这个操作操作时不安全的,因此线程必须保证是活的.这是java代码不是native代码。 + */ + public native void unpark(Thread thread); + /*** + * 阻塞一个线程直到unpark出现、线程 + * 被中断或者timeout时间到期。如果一个unpark调用已经出现了, + * 这里只计数。timeout为0表示永不过期.当isAbsolute为true时, + * timeout是相对于新纪元之后的毫秒。否则这个值就是超时前的纳秒数。这个方法执行时 + * 也可能不合理地返回(没有具体原因) + */ + public native void park(boolean isAbsolute, long time); +} +``` + + + -- Gitee