From 486612f169f02fb8708352aa5930d30210e878df Mon Sep 17 00:00:00 2001 From: zhongtao9527 <765426290@qq.com> Date: Sun, 22 Dec 2019 23:18:30 +0800 Subject: [PATCH] =?UTF-8?q?026=E5=8F=B7=E5=AD=A6=E9=99=A2=E7=AC=AC?= =?UTF-8?q?=E4=BA=8C=E5=91=A8=E7=AC=94=E8=AE=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- week_02/26/AtomicInteger-026.md | 57 ++++++ week_02/26/AtomicStampedReference-026.md | 188 +++++++++++++++++ week_02/26/LongAdder-026.md | 197 ++++++++++++++++++ week_02/26/Unsafe-026.md | 244 +++++++++++++++++++++++ 4 files changed, 686 insertions(+) create mode 100644 week_02/26/AtomicInteger-026.md create mode 100644 week_02/26/AtomicStampedReference-026.md create mode 100644 week_02/26/LongAdder-026.md create mode 100644 week_02/26/Unsafe-026.md diff --git a/week_02/26/AtomicInteger-026.md b/week_02/26/AtomicInteger-026.md new file mode 100644 index 0000000..b134030 --- /dev/null +++ b/week_02/26/AtomicInteger-026.md @@ -0,0 +1,57 @@ +# AtomicInteger +## 简单说明 +#### AtomicInteger是Java的原子整数,它可以用在多线程环境下,并且不会造成数据不一致的问题。AtomicInteger是一个可以原子更新的整形数值。AtomicInteger可以用在作为原子自增的计数器类似的应用中,但是不能作为Integer的替代。 +## 成员变量 +```Java +private static final Unsafe unsafe = Unsafe.getUnsafe(); + private static final long valueOffset; + + static { + try { + valueOffset = unsafe.objectFieldOffset + (AtomicInteger.class.getDeclaredField("value")); + } catch (Exception ex) { throw new Error(ex); } + } + + private volatile int value; +``` +AtomicInteger使用了Unsafe类来执行CAS操作。value保存实际的整数值,它被volatile修饰。volatile相当于synchronized的弱实现,也就是说volatile实现了类似synchronized的语义,却又没有锁机制。它确保对volatile字段的更新以可预见的方式告知其他的线程。在静态代码块中,通过unsafe的方法,获取到value属性在内存中的地址,并保存在valueOffset中。 +## 简单方法 +```Java + public AtomicInteger(int initialValue) { + value = initialValue; + } + + public AtomicInteger() { + } + + public final int get() { + return value; + } + + public final void set(int newValue) { + value = newValue; + } +``` +#### lazySet +```Java +public final void lazySet(int newValue) { + unsafe.putOrderedInt(this, valueOffset, newValue); + } +``` +当有多个线程同时调用lazySet时,会根据线程的调用顺序进行排队,依次设置各个线程调用该函数时传递的参数。 +#### getAndSet +```Java +public final int getAndSet(int newValue) { + return unsafe.getAndSetInt(this, valueOffset, newValue); + } + 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; + } +``` +getAndSet函数。它的作用是原子的设置为新值,并返回旧值。从代码可以看出,它直接调用unsafe的getAndSetInt函数来实现。getAndSet的源码里面是一个do-while结构,每次先获取int值的最新值,然后执行compareAndSwapInt函数,将int值设置为新的value值。如果失败,那么再重新获取int的值,继续同样的操作,直到成功。当线程1获取了旧的int值之后,如果有线程2得到执行,将int值设置为了新的newValue2,那么当线程1再执行compareAndSwapInt函数时,就会返回false;也正是通过compareAndSwapInt函数,来保证了对值的修改的原子性。 \ No newline at end of file diff --git a/week_02/26/AtomicStampedReference-026.md b/week_02/26/AtomicStampedReference-026.md new file mode 100644 index 0000000..9bdd162 --- /dev/null +++ b/week_02/26/AtomicStampedReference-026.md @@ -0,0 +1,188 @@ +# AtomicInteger +## 简单说明 +#### AtomicStampedReference是java并发包下提供的一个原子类,为了避免CAS过程中的ABA问题,并发包提供了两个类,AtomicStampedReference和AtomicMarkableReference。前者相当于一个[引用,integer]的二元组,后者相当于一个[引用,boolean]的二元组。AtomicStampedReference可用来作为带版本号的原子引用,而AtomicMarkableReference可用于表示如:已删除的节点 +## 什么是ABA +ABA问题发生在多线程环境中,当某线程连续读取同一块内存地址两次,两次得到的值一样,它简单地认为“此内存地址的值并没有被修改过”,然而,同时可能存在另一个线程在这两次读取之间把这个内存地址的值从A修改成了B又修改回了A,这时还简单地认为“没有修改过”显然是错误的。 +## 内部结构 +```Java +/** + * AtomicStampedReference 维护带有整数“标志”的对象引用,可以用原子方式对其进行更新。 + * + * 实现注意事项。通过创建表示“已装箱”的 [reference, integer] 对的内部对象,此实现维持带标志的引用。 + * @param 此引用引用的对象的类型 + */ +public class AtomicStampedReference { + private static class Pair { + final T reference; + final int stamp; + + /** + * + * @param reference 引用 + * @param stamp 标志 + */ + private Pair(T reference, int stamp) { + this.reference = reference; + this.stamp = stamp; + } + + /** + * 静态工厂 + * @param reference 引用 + * @param stamp 标志 + * @param 此引用引用的对象的类型 + * @return + */ + static Pair of(T reference, int stamp) { + return new Pair(reference, stamp); + } + } + + private volatile Pair pair; + + /** + * 创建具有给定初始值的新 AtomicStampedReference。 + * @param initialRef 初始引用 + * @param initialStamp 初始标志 + */ + public AtomicStampedReference(V initialRef, int initialStamp) { + pair = Pair.of(initialRef, initialStamp); + } +} +``` +#### AtomicStampedReference内部是一个ReferenceIntegerPair引用(元组),然后使用一个AtomicReference来对这个引用进行原子更新 +## 其他方法 +```Java + + /** + * 返回该引用的当前值。 + * @return 该引用的当前值 + */ + public V getReference() { + return pair.reference; + } + + /** + * 返回该标志的当前值。 + * @return 该标志的当前值 + */ + public int getStamp() { + return pair.stamp; + } + + /** + * 返回该引用和该标志的当前值。典型的用法为 int[1] holder; ref = v.get(holder); 。 + * @param stampHolder 大小至少为 1 的数组。返回时,stampholder[0] 将保存该标志的值。 + * @return 该引用的当前值 + */ + public V get(int[] stampHolder) { + Pair pair = this.pair; + stampHolder[0] = pair.stamp; + return pair.reference; + } + + /** + * 如果当前引用 == 预期引用,并且当前标志等于预期标志,则以原子方式将该引用和该标志的值设置为给定的更新值。 + * 可能意外失败并且不提供排序保证,所以只有在很少的情况下才对 compareAndSet 进行适当的选择。 + * @param expectedReference 该引用的预期值 + * @param newReference 该引用的新值 + * @param expectedStamp 该标志的预期值 + * @param newStamp 该标志的新值 + * @return 如果成功,则返回 true + */ + public boolean weakCompareAndSet(V expectedReference, + V newReference, + int expectedStamp, + int newStamp) { + return compareAndSet(expectedReference, newReference, + expectedStamp, newStamp); + } + + /** + * 如果当前引用 == 预期引用,并且当前标志等于预期标志,则以原子方式将该引用和该标志的值设置为给定的更新值。 + * @param expectedReference 该引用的预期值 + * @param newReference 该引用的新值 + * @param expectedStamp 该标志的预期值 + * @param newStamp 该标志的新值 + * @return 如果成功,则返回 true + */ + 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))); + } + + /** + * 无条件地同时设置该引用和标志的值。 + * @param newReference 该引用的新值 + * @param newStamp 该标志的新值 + */ + public void set(V newReference, int newStamp) { + Pair current = pair; + if (newReference != current.reference || newStamp != current.stamp) + this.pair = Pair.of(newReference, newStamp); + } + + /** + * 如果当前引用 == 预期引用,则以原子方式将该标志的值设置为给定的更新值。此操作的任何给定调用都可能会意外失败(返回 false), + * 但是在当前值保持预期值而且没有其他线程也在尝试设置该值时,重复调用将最终获得成功。 + * @param expectedReference 该引用的预期值 + * @param newStamp 该标志的新值 + * @return 如果成功,则返回 true + */ + public boolean attemptStamp(V expectedReference, int newStamp) { + Pair current = pair; + return + expectedReference == current.reference && + (newStamp == current.stamp || + casPair(current, Pair.of(expectedReference, newStamp))); + } + + // 不安全机制 + private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe(); + private static final long pairOffset = + objectFieldOffset(UNSAFE, "pair", AtomicStampedReference.class); + + /** + * pair比对 + * @param cmp + * @param val + * @return + */ + private boolean casPair(Pair cmp, Pair val) { + return UNSAFE.compareAndSwapObject(this, pairOffset, cmp, val); + } + + /** + * 获取偏移量 + * @param UNSAFE + * @param field 字段名 + * @param klazz 类型 + * @return + */ + static long objectFieldOffset(sun.misc.Unsafe UNSAFE, + String field, Class klazz) { + try { + return UNSAFE.objectFieldOffset(klazz.getDeclaredField(field)); + } catch (NoSuchFieldException e) { + // 将异常转换为相应的错误 + NoSuchFieldError error = new NoSuchFieldError(field); + error.initCause(e); + throw error; + } + } + +``` +## 总结 +(1)在多线程环境下使用无锁结构要注意ABA问题; + +(2)ABA的解决一般使用版本号来控制,并保证数据结构使用元素值来传递,且每次添加元素都新建节点承载元素值; + +(3)AtomicStampedReference内部使用Pair来存储元素值及其版本号; \ No newline at end of file diff --git a/week_02/26/LongAdder-026.md b/week_02/26/LongAdder-026.md new file mode 100644 index 0000000..0d1317d --- /dev/null +++ b/week_02/26/LongAdder-026.md @@ -0,0 +1,197 @@ +# LongAdder +## 简单说明 +#### 并发量并不多,而且等待时间不长的情况,使用CAS进行适合轻量级的并发操作。 +#### 有一个Long类型的值会被多线程修改,一般都使用AtomicLong。AtomicLong是通过无限循环不停的采取CAS的方法去设置内部的value,直到成功为止。那么当并发数比较多或出现更新热点时,就会导致CAS的失败机率变高,重试次数更多,越多的线程重试,CAS失败的机率越高,形成恶性循环,从而降低了效率。 +#### AtomicLong效率降低,怎么解决?LongAdder的原理就是降低对value更新的并发数,也就是将对单一value的变更压力分散到多个value值上,降低单个value的“热度”。 +## 成员变量 +```Java +// CPU的数量 +static final int NCPU = Runtime.getRuntime().availableProcessors(); +// Cell对象的数组,长度一般是2的指数 +transient volatile Cell[] cells; +// 基础value值,当并发较低时,只累加该值 +transient volatile long base; +// 创建或者扩容Cells数组时使用的自旋锁变量 +transient volatile int cellsBusy; +``` +cells是LongAdder的父类Striped64中的Cell数组类型的成员变量。每个Cell对象中都包含一个value值,并提供对这个value值的CAS操作。 +```Java +static final class Cell { + volatile long value; + Cell(long x) { value = x; } + final boolean cas(long cmp, long val) { + return UNSAFE.compareAndSwapLong(this, valueOffset, cmp, val); + } + private static final sun.misc.Unsafe UNSAFE; + private static final long valueOffset; + static { + try { + UNSAFE = sun.misc.Unsafe.getUnsafe(); + Class ak = Cell.class; + valueOffset = UNSAFE.objectFieldOffset + (ak.getDeclaredField("value")); + } catch (Exception e) { + throw new Error(e); + } + } +} +``` +## sum 方法 +```Java +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; + } +``` +将多个cell数组中的值加起来的和 +## Add方法 +```Java +public void add(long x) { + Cell[] as; long b, v; int m; Cell a; + // 当cells数组为null时,会进行第一次cas操作尝试。 + 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))) + // 当cells数组不为null,并且通过getProbe() & m + // 定位的Cell对象不为null时进行第二次CAS操作。 + // 如果执行不成功,则进入longAccumulate函数。 + longAccumulate(x, null, uncontended); + } +} +``` +```Java +final boolean casBase(long cmp, long val) { + return UNSAFE.compareAndSwapLong(this, BASE, cmp, val); +} +``` +变量base就是第一个value值,也是基础value变量。先调用casBase函数来cas一下base变量,如果成功了,就不需要在进行下面的算法, +当并发量较少时,cell数组尚未初始化,所以只调用casBase函数,对base变量进行CAS累加。当并发量逐渐提高时,casBase函数会失败。如果cells数组为null或为空,就直接调用longAccumulate方法。因为cells为null或在为空,说明cells未初始化,所以调用longAccumulate进行初始化。否则继续判断。 +## LongAccumulate +```Java + + /** + * 处理涉及初始化,调整大小,创建新Cell,和/或争用的更新案例 + * + * @param x 值 + * @param fn 更新方法 + * @param wasUncontended 调用 + */ + final void longAccumulate(long x, LongBinaryOperator fn, boolean wasUncontended) { + int h; + // 获取线程probe的值 + if ((h = getProbe()) == 0) { + // 值为0则初始化 + ThreadLocalRandom.current(); //强制初始化 + h = getProbe(); + wasUncontended = true; + } + boolean collide = false; // True if last slot nonempt + for (;;) { + Cell[] as; Cell a; int n; long v; + // 这个if分支处理上述四个条件中的前两个相似,此时cells数组已经初始化了并且长度大于0 + if ((as = cells) != null && (n = as.length) > 0) { + // 线程对应的cell为null + if ((a = as[(n - 1) & h]) == null) { + // 如果busy锁未被占有 + if (cellsBusy == 0) { // Try to attach new Cell + // 新建一个cell + Cell r = new Cell(x); // Optimistically create + // 检测busy是否为0,并且尝试锁busy + if (cellsBusy == 0 && casCellsBusy()) { + boolean created = false; + try { // Recheck under lock + Cell[] rs; int m, j; + //再次确认线程probe所对应的cell为null,将新建的cell赋值 + if ((rs = cells) != null && + (m = rs.length) > 0 && + rs[j = (m - 1) & h] == null) { + rs[j] = r; + created = true; + } + } finally { + // 解锁 + cellsBusy = 0; + } + if (created) + break; + //如果失败,再次尝试 + continue; // Slot is now non-empty + } + } + collide = false; + } + //置为true后交给循环重试 + else if (!wasUncontended) // CAS already known to fail + wasUncontended = true; // Continue after rehash + //尝试给线程对应的cell update + else if (a.cas(v = a.value, ((fn == null) ? v + x : + fn.applyAsLong(v, x)))) + break; + else if (n >= NCPU || cells != as) + collide = false; // At max size or stale + else if (!collide) + collide = true; + //在以上条件都无法解决的情况下尝试扩展cell + else if (cellsBusy == 0 && casCellsBusy()) { + try { + if (cells == as) { // Expand table unless stale + Cell[] rs = new Cell[n << 1]; + for (int i = 0; i < n; ++i) + rs[i] = as[i]; + cells = rs; + } + } finally { + cellsBusy = 0; + } + collide = false; + continue; // Retry with expanded table + } + h = advanceProbe(h); + } + //此时cells还未进行第一次初始化,进行初始化 + else if (cellsBusy == 0 && cells == as && casCellsBusy()) { + boolean init = false; + try { // Initialize table + if (cells == as) { + Cell[] rs = new Cell[2]; + rs[h & 1] = new Cell(x); + cells = rs; + init = true; + } + } finally { + cellsBusy = 0; + } + if (init) + break; + } + //busy锁不成功或者忙,则再重试一次casBase对value直接累加 + else if (casBase(v = base, ((fn == null) ? v + x : + fn.applyAsLong(v, x)))) + break; // Fall back on using base + } + } + /** + * Spinlock (locked via CAS) used when resizing and/or creating Cells. + * 通过cas实现的自旋锁,用于扩大或者初始化cells + */ + transient volatile int cellsBusy; +``` + +## 使用场景 +适用于统计求和计数的场景,因为它提供了add、sum方法 + +## LongAdder是否能够替换AtomicLong +从上面的分析来看是不行的,因为AtomicLong提供了很多cas方法,例如getAndIncrement、getAndDecrement等,使用起来非常的灵活,而LongAdder只有add和sum,使用起来比较受限。 + +优点:由于 JVM 会将 64位的double,long 型变量的读操作分为两次32位的读操作,所以低并发保持了 AtomicLong性能,高并发下热点数据被 hash 到多个 Cell,有限分离,通过分散提升了并行度 + +但统计时有数据更新,也可能会出现数据误差,但高并发场景有限使用此类,低时还是可以继续 AtomicLong \ No newline at end of file diff --git a/week_02/26/Unsafe-026.md b/week_02/26/Unsafe-026.md new file mode 100644 index 0000000..3b2b1e2 --- /dev/null +++ b/week_02/26/Unsafe-026.md @@ -0,0 +1,244 @@ +# Unsafe +## 简单说明 +#### java是没有指针的,默认是由JVM进行内存的分配与垃圾回收,这时候就需要Unsafe类。UnSafe提供了硬件级别的原子操作,Unsafe类通过JNI的方式访问本地的C++实现库从而使java具有了直接操作内存空间的能力,但这同时也带来了一定的问题,如果不合理地使用Unsafe类操作内存空间,可能导致内存泄漏 +## 获取Unsafe +```Java +private Unsafe() {} + +private static final Unsafe theUnsafe = new Unsafe(); + +@CallerSensitive +public static Unsafe getUnsafe() { + Class caller = Reflection.getCallerClass(); + if (!VM.isSystemDomainLoader(caller.getClassLoader())) + throw new SecurityException("Unsafe"); + return theUnsafe; +} +``` +#### isSystemDomainLoader(caller.getClassLoader())方法检查调用者的类加载器是否是启动类加载器,Unsafe类只能是默认由系统类加载器加载。使用反射可以获取到Unsafe实例。 +```Java +public static void main(String[] args) { + Field f; + sun.misc.Unsafe unsafe = null; + try { + // theUnsafe是Unsafe类的static final方法,在Unsafe类的static方法快就已经初始化赋值了。 + f = Unsafe.class.getDeclaredField("theUnsafe"); + f.setAccessible(true); + // 得到Unsafe类的实例,因为是静态变量,所以不需要提供对象,直接传参null获取 + unsafe = (Unsafe) f.get(null); + } catch (NoSuchFieldException | IllegalAccessException e) { + e.printStackTrace(); + } + } +``` +## 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是唯一的,并且后续对该方法的调用都应该 + * 返回相同的值。 + * @param field 需要返回偏移量的field + * @return 指定field的偏移量 + */ + public native long objectFieldOffset(Field field); + /*** + * 在obj的offset位置比较integer field和期望的值,如果相同则更新。这个方法 + * 的操作应该是原子的,因此提供了一种不可中断的方式更新integer field。 + * @param obj 包含要修改field的对象 + * @param offset obj中整型field的偏移量 + * @param expect 希望field中存在的值 + * @param update 如果期望值expect与field的当前值相同,设置filed的值为这个新值 + * @return true 如果field的值被更改 + */ + public native boolean compareAndSwapInt(Object obj, long offset, + int expect, int update); + /*** + * 在obj的offset位置比较long field和期望的值,如果相同则更新。这个方法 + * 的操作应该是原子的,因此提供了一种不可中断的方式更新long field。 + * @param obj 包含要修改field的对象 + * @param offset obj中long型field的偏移量 + * @param expect 希望field中存在的值 + * @param update 如果期望值expect与field的当前值相同,设置filed的值为这个新值 + * @return true 如果field的值被更改 + */ + public native boolean compareAndSwapLong(Object obj, long offset, + long expect, long update); + /*** + * 在obj的offset位置比较object field和期望的值,如果相同则更新。这个方法 + * 的操作应该是原子的,因此提供了一种不可中断的方式更新object field。 + * @param obj 包含要修改field的对象 + * @param offset obj中object型field的偏移量 + * @param expect 希望field中存在的值 + * @param update 如果期望值expect与field的当前值相同,设置filed的值为这个新值 + * @return true 如果field的值被更改 + */ + public native boolean compareAndSwapObject(Object obj, long offset, + Object expect, Object update); + /*** + * 设置obj对象中offset偏移地址对应的整型field的值为指定值。这是一个有序或者 + * 有延迟的putIntVolatile方法,并且不保证值的改变被其他线程立 + * 即看到。只有在field被volatile修饰并且期望被意外修改的时候 + * 使用才有用。 + * @param obj 包含需要修改field的对象 + * @param offset obj中整型field的偏移量 + * @param value field将被设置的新值 + * @see 看对应方法 #putIntVolatile(Object,long,int) + */ + public native void putOrderedInt(Object obj, long offset, int value); + /*** + * 设置obj对象中offset偏移地址对应的long型field的值为指定值。这是一个有序或者 + * 有延迟的putLongVolatile方法,并且不保证值的改变被其他线程立 + * 即看到。只有在field被volatile修饰并且期望被意外修改的时候 + * 使用才有用。 + * + * @param obj 包含需要修改field的对象 + * @param offset obj中long型field的偏移量 + * @param value field将被设置的新值 + * @see 看对应方法 #putLongVolatile(Object,long,long) + */ + public native void putOrderedLong(Object obj, long offset, long value); + /*** + * 设置obj对象中offset偏移地址对应的object型field的值为指定值。这是一个有序或者 + * 有延迟的putObjectVolatile方法,并且不保证值的改变被其他线程立 + * 即看到。只有在field被volatile修饰并且期望被意外修改的时候 + * 使用才有用。 + * @param obj 包含需要修改field的对象 + * @param offset obj中long型field的偏移量 + * @param value field将被设置的新值 + */ + public native void putOrderedObject(Object obj, long offset, Object value); + /*** + * Sets the value of the integer field at the specified offset in the + * supplied object to the given value, with volatile store semantics. + * 设置obj对象中offset偏移地址对应的整型field的值为指定值。支持volatile store语义 + * @param obj 包含需要修改field的对象 + * @param offset obj中整型field的偏移量 + * @param value field将被设置的新值 + */ + public native void putIntVolatile(Object obj, long offset, int value); + /*** + * 获取obj对象中offset偏移地址对应的整型field的值,支持volatile load语义。 + * + * @param obj 包含需要去读取的field的对象 + * @param offset obj中整型field的偏移量 + */ + public native int getIntVolatile(Object obj, long offset); + /*** + * 设置obj对象中offset偏移地址对应的long型field的值为指定值。支持volatile store语义 + * + * @param obj 包含需要修改field的对象 + * @param offset obj中long型field的偏移量 + * @param value field将被设置的新值 + * @see 看对应方法 #putLong(Object,long,long) + */ + public native void putLongVolatile(Object obj, long offset, long value); + /*** + * 设置obj对象中offset偏移地址对应的long型field的值为指定值。 + * + * @param obj 包含需要修改field的对象 + * @param offset obj中long型field的偏移量 + * @param value field将被设置的新值 + * @see 看对应方法 #putLongVolatile(Object,long,long) + */ + public native void putLong(Object obj, long offset, long value); + /*** + * 获取obj对象中offset偏移地址对应的long型field的值,支持volatile load语义。 + * @param obj 包含需要去读取的field的对象 + * @param offset obj中long型field的偏移量 + * @see 看对应方法 #getLong(Object,long) + */ + public native long getLongVolatile(Object obj, long offset); + /*** + * 获取obj对象中offset偏移地址对应的long型field的值 + * @param obj 包含需要去读取的field的对象 + * @param offset obj中long型field的偏移量 + * @see 看对应方法 #getLongVolatile(Object,long) + */ + public native long getLong(Object obj, long offset); + /*** + * 设置obj对象中offset偏移地址对应的object型field的值为指定值。支持volatile store语义 + * @param obj 包含需要修改field的对象 + * @param offset obj中object型field的偏移量 + * @param value field将被设置的新值 + * @see 看对应方法 #putObject(Object,long,Object) + */ + public native void putObjectVolatile(Object obj, long offset, Object value); + /*** + * 设置obj对象中offset偏移地址对应的object型field的值为指定值。 + * @param obj 包含需要修改field的对象 + * @param offset obj中object型field的偏移量 + * @param value field将被设置的新值 + * @see 看对应方法 #putObjectVolatile(Object,long,Object) + */ + public native void putObject(Object obj, long offset, Object value); + /*** + * 获取obj对象中offset偏移地址对应的object型field的值,支持volatile load语义。 + * @param obj 包含需要去读取的field的对象 + * @param offset obj中object型field的偏移量 + */ + public native Object getObjectVolatile(Object obj, long offset); + /*** + * 获取给定数组中第一个元素的偏移地址。 + * 为了存取数组中的元素,这个偏移地址与arrayIndexScale + * 方法的非0返回值一起被使用。 + * @param arrayClass 第一个元素地址被获取的class + * @return 数组第一个元素 的偏移地址 + * @see 看对应方法 #arrayIndexScale(Class) + */ + public native int arrayBaseOffset(Class arrayClass); + /*** + * 获取用户给定数组寻址的换算因子.一个合适的换算因子不能返回的时候(例如:基本类型), + * 返回0.这个返回值能够与arrayBaseOffset + * 一起使用去存取这个数组class中的元素 + */ + public native int arrayIndexScale(Class arrayClass); + + /*** + * 释放被park创建的在一个线程上的阻塞.这个 + * 方法也可以被使用来终止一个先前调用park导致的阻塞. + * 这个操作操作时不安全的,因此线程必须保证是活的.这是java代码不是native代码。 + * @param thread the thread to unblock. + * 要解除阻塞的线程 + */ + public native void unpark(Thread thread); + /*** + * 阻塞一个线程直到unpark出现、线程 + * 被中断或者timeout时间到期。如果一个unpark调用已经出现了, + * 这里只计数。timeout为0表示永不过期.当isAbsolute为true时, + * timeout是相对于新纪元之后的毫秒。否则这个值就是超时前的纳秒数。这个方法执行时 + * 也可能不合理地返回(没有具体原因) + * @param isAbsolute 如果为true timeout的值是一个相对于新纪元之后的毫秒数 + * @param time 可以是一个要等待的纳秒数,或者是一个相对于新纪元之后的毫秒数直到 + * 到达这个时间点 + */ + public native void park(boolean isAbsolute, long time); +} +``` \ No newline at end of file -- Gitee