diff --git a/week_02/23/AtomicInteger.md b/week_02/23/AtomicInteger.md new file mode 100644 index 0000000000000000000000000000000000000000..f82e9d1bbe5b1caaf36dda0ebd9542ac1d30d40d --- /dev/null +++ b/week_02/23/AtomicInteger.md @@ -0,0 +1,113 @@ +#### 构造函数 + +```java +private volatile int value; +/* + * AtomicInteger内部声明了一个volatile修饰的变量value用来保存实际值 + * 使用带参的构造函数会将入参赋值给value,无参构造器value默认值为0 + */ +public AtomicInteger(int initialValue) { + value = initialValue; +} +``` + +#### 自增函数 + +```java +import sun.misc.Unsafe; +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); } +} +/* + * 可以看到自增函数中调用了Unsafe函数的getAndAddInt方法 + */ +public final int incrementAndGet() { + return unsafe.getAndAddInt(this, valueOffset, 1) + 1; +} + + +/* + * 其中getIntVolatile和compareAndSwapInt都是native方法 + * getIntVolatile是获取当前的期望值 + * compareAndSwapInt就是我们平时说的CAS(compare and swap),通过比较如果内存区的值没有改变,那么就用新值直接给该内存区赋值 + */ +public final int getAndAddInt(Object paramObject, long paramLong, int paramInt) +{ + int i; + do + { + i = getIntVolatile(paramObject, paramLong); + } while (!compareAndSwapInt(paramObject, paramLong, i, i + paramInt)); + return i; +} +``` + +#### 测试代码 + +```java +public class AtomicIntegerTest { + + private static int n = 0; + + public static void main(String[] args) throws InterruptedException { + //i++引发的线程问题 + Thread t1 = new Thread() { + public void run() { + for(int i = 0; i < 1000; i++) { + n++; + } + }; + }; + Thread t2 = new Thread() { + public void run() { + for(int i = 0; i < 1000; i++) { + n++; + } + }; + }; + t1.start(); + t2.start(); + t1.join(); + t2.join(); + System.out.println("最终n的值为:" + n); + } +} +``` + +如果i++是原子操作,那么结果应该就是2000,反复运行几次发现结果大部分情况下都不是2000,这也证明了i++的非原子性在多线程下产生的问题。 + +```java +public class AtomicIntegerTest { + + private static AtomicInteger n2 = new AtomicInteger(0); + + public static void main(String[] args) throws InterruptedException { + Thread t1 = new Thread() { + public void run() { + for(int i = 0; i < 1000; i++) { + n2.incrementAndGet(); + } + }; + }; + Thread t2 = new Thread() { + public void run() { + for(int i = 0; i< 1000; i++) { + n2.incrementAndGet(); + } + } + }; + t1.start(); + t2.start(); + t1.join(); + t2.join(); + System.out.println("最终n2的值为:" + n2.toString()); + } +} +``` + +多次运行,发现结果永远是2000,由此可以证明AtomicInteger的操作是原子性的。 \ No newline at end of file diff --git a/week_02/23/AtomicStampedReference.md b/week_02/23/AtomicStampedReference.md new file mode 100644 index 0000000000000000000000000000000000000000..1b3d854402ea753cacd00284f6bebeebab3e8da8 --- /dev/null +++ b/week_02/23/AtomicStampedReference.md @@ -0,0 +1,182 @@ +## CAS中ABA问题 + +由于提款机硬件出了点小问题,小灰的提款操作被同时提交两次,开启了两个线程,两个线程都是获取当前值100元,要更新成50元。 + +理想情况下,应该一个线程更新成功,另一个线程更新失败,小灰的存款只被扣一次。 + +![](F:\git\personal\pic\20190831222724580.png) + +线程1首先执行成功,把余额从100改成50。线程2因为某种原因阻塞了。**这时候,小灰的妈妈刚好给小灰汇款50元**。 + +![](F:\git\personal\pic\ABA2.png) + +线程2仍然是阻塞状态,线程3执行成功,把余额从50改成100。 + +![](F:\git\personal\pic\ABA3.png) + +线程2恢复运行,由于阻塞之前已经获得了“当前值”100,并且经过compare检测,此时存款实际值也是100,所以成功把变量值100更新成了50。 + +![](F:\git\personal\pic\ABA4.png) + +本线程2应当提交失败,小灰的正确余额应该保持为100元,结果由于ABA问题提交成功了。 + +`AtomicMarkableReference`是带布尔型标记为的原子引用类型,为了同时兼顾引用值和标记位,它定义了一个静态内部类`Pair`,`AtomicMarkableReference`的相关操作都是对`Pair`内成员的操作。 + +```java +private static class Pair { + final T reference; + final boolean mark; + private Pair(T reference, boolean mark) { + this.reference = reference; + this.mark = mark; + } + static Pair of(T reference, int mark) { + return new Pair(reference, mark); + } +} +``` + +## 成员变量 + +`AtomicMarkableReference`除了常规的`sun.misc.Unsafe`实例和`pairOffset`内存偏移量外,声明了一个`volatile`的`Pair`成员,用于同时维护引用值和标记值。 + +```java +// 成员变量unsafe是原子变量相关操作的基础 +// 原子变量的修改操作最终有sun.misc.Unsafe类的CAS操作实现 +private static final sun.misc.Unsafe UNSAFE = Unsafe.getUnsafe(); +// 成员变量value的内存偏移值 +private static final long pairOffset = objectFieldOffset(UNSAFE, "pair", AtomicMarkableReference.class); +// 用volatile的内存语义保证可见性 +// 保存引用值和标记值 +private volatile Pair pair; + +// 获取指定域的内存偏移量 +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; + } +} +``` + + + +```java +// 构造函数,初始化引用和标记值 +public AtomicMarkableReference(V initialRef, boolean initialMark) +// 以原子方式获取当前引用值 +public V getReference() +// 以原子方式获取当前标记值 +public int isMarked() +// 以原子方式获取当前引用值和标记值 +public V get(boolean[] markHolder) +// 以原子的方式同时更新引用值和标记值 +// 当期望引用值不等于当前引用值时,操作失败,返回false +// 当期望标记值不等于当前标记值时,操作失败,返回false +// 在期望引用值和期望标记值同时等于当前值的前提下 +// 当新的引用值和新的标记值同时等于当前值时,不更新,直接返回true +// 当新的引用值和新的标记值不同时等于当前值时,同时设置新的引用值和新的标记值,返回true +// 该过程不保证volatile成员的happens-before语义顺序 +public boolean weakCompareAndSet(V expectedReference, + V newReference, + int expectedMark, + int newMark) +// 以原子的方式同时更新引用值和标记值 +// 当期望引用值不等于当前引用值时,操作失败,返回false +// 当期望标记值不等于当前标记值时,操作失败,返回false +// 在期望引用值和期望标记值同时等于当前值的前提下 +// 当新的引用值和新的标记值同时等于当前值时,不更新,直接返回true +// 当新的引用值和新的标记值不同时等于当前值时,同时设置新的引用值和新的标记值,返回true +public boolean compareAndSet(V expectedReference, + V newReference, + int expectedMark, + int newMark) +// 以原子方式设置引用的当前值为新值newReference +// 同时,以原子方式设置标记值的当前值为新值newMark +// 新引用值和新标记值只要有一个跟当前值不一样,就进行更新 +public void set(V newReference, int newMark) +// 以原子方式设置标记值为新的值 +// 前提:引用值保持不变 +// 当期望的引用值与当前引用值不相同时,操作失败,返回fasle +// 当期望的引用值与当前引用值相同时,操作成功,返回true +public boolean attemptMark(V expectedReference, int newMark) +// 使用`sun.misc.Unsafe`类原子地交换两个对象 +private boolean casPair(Pair cmp, Pair val) +``` + +```java +public V get(int[] markHolder) { + Pair pair = this.pair; + markHolder[0] = pair.markz; + return pair.reference; +} +``` + +同时获取引用值和标记值。由于Java程序只能有一个返回值,该函数通过一个数组参数`int[] markHolder`来返回标记值,而通过`return`语句返回引用值。 + +```java +public void set(V newReference, int newMark) { + Pair current = pair; + if (newReference != current.reference || newMark != current.mark) + this.pair = Pair.of(newReference, newMark); +} +``` + +只要新的引用值和新的标记值,有一个与当前值不一样的,就同时修改引用值和标记值。 + +```java +public boolean weakCompareAndSet(V expectedReference, + V newReference, + int expectedMark, + int newMark) { + return compareAndSet(expectedReference, newReference, + expectedMark, newMark); +} +``` + +以原子的方式同时更新引用值和标记值。该是通过调用CompareAndSet实现的。JDK文档中说,weakCompareAndSet在更新变量时并不创建任何`happens-before`顺序,因此即使要修改的值是volatile的,也不保证对该变量的读写操作的顺序(一般来讲,volatile的内存语义保证`happens-before`顺序)。 + +```java +public boolean attemptMark(V expectedReference, int newMark) { + Pair current = pair; + return + expectedReference == current.reference && + (newMark == current.mark || + casPair(current, Pair.of(expectedReference, newMark))); +} +``` + +修改指定引用值的标记值。 + +当期望的引用值与当前引用值不相同时,操作失败,返回fasle。 +当期望的引用值与当前引用值相同时,操作成功,返回true。 + +```java +public boolean compareAndSet(V expectedReference, + V newReference, + int expectedMark, + int newMark) { + Pair current = pair; + return + expectedReference == current.reference && + expectedMark == current.mark && + ((newReference == current.reference && + newMark == current.mark) || + casPair(current, Pair.of(newReference, newMark))); + } +``` + +以原子的方式同时更新引用值和标记值。 + +当期望引用值不等于当前引用值时,操作失败,返回false。 + +当期望标记值不等于当前标记值时,操作失败,返回false。 + +在期望引用值和期望标记值同时等于当前值的前提下,当新的引用值和新的标记值同时等于当前值时,不更新,直接返回true。由于要修改的内容与原内容完全一致,这种处理可以避免一次内存操作,效率较高。 + +当新的引用值和新的标记值不同时等于当前值时,同时设置新的引用值和新的标记值,返回true \ No newline at end of file diff --git a/week_02/23/LongAdder.md b/week_02/23/LongAdder.md new file mode 100644 index 0000000000000000000000000000000000000000..b919ed21bd6b8d68600bff3c6dfb521c79c2f3e9 --- /dev/null +++ b/week_02/23/LongAdder.md @@ -0,0 +1,198 @@ +## LongAdder类结构 + +```java +public class LongAdder extends Striped64 implements Serializable { + private static final long serialVersionUID = 7249069246863182397L; + /** + * Creates a new adder with initial sum of zero. + */ + public LongAdder() { + } +``` + + + + +```java +public void add(long x) { + Cell[] as; long b, v; int m; Cell a; + //如果cell表为null,会尝试将x累加到base上。 + if ((as = cells) != null || !casBase(b = base, b + x)) { + /* + * 如果cell表不为null或者尝试将x累加到base上失败,执行以下操作。 + * 如果cell表不为null且通过当前线程的probe值定位到的cell表中的Cell不为null。 + * 那么尝试累加x到对应的Cell上。 + */ + boolean uncontended = true; + if (as == null || (m = as.length - 1) < 0 || + (a = as[getProbe() & m]) == null || + !(uncontended = a.cas(v = a.value, v + x))) + //或者cell表为null,或者定位到的cell为null,或者尝试失败,都会调用下面的Striped64中定义的longAccumulate方法。 + longAccumulate(x, null, uncontended); + } +} +``` + + add方法逻辑很简单,先尝试将x累加到base上,失败的话再看看能不能从cell表中找到cell,找到的话再尝试将x累加到这个cell里面,还失败的话就调用longAccumulate方法,这个方法上篇分析Striped64的时候分析过。 + + + +```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; +} +``` + +​ sum方法就是获取当前LongAdder值的总和,包括base和cells value两部分。 + +## Striped64主体代码 + + + +```java +abstract class Striped64 extends Number { + @sun.misc.Contended static final class Cell { ... } + + /** Number of CPUS, to place bound on table size */ + static final int NCPU = Runtime.getRuntime().availableProcessors(); + + // cell数组,长度一样要是2^n,可以类比为jdk1.7的ConcurrentHashMap中的segments数组 + transient volatile Cell[] cells; + + // 累积器的基本值,在两种情况下会使用: + // 1、没有遇到并发的情况,直接使用base,速度更快; + // 2、多线程并发初始化table数组时,必须要保证table数组只被初始化一次,因此只有一个线程能够竞争成功,这种情况下竞争失败的线程会尝试在base上进行一次累积操作 + transient volatile long base; + + // 自旋标识,在对cells进行初始化,或者后续扩容时,需要通过CAS操作把此标识设置为1(busy,忙标识,相当于加锁),取消busy时可以直接使用cellsBusy = 0,相当于释放锁 + transient volatile int cellsBusy; + + Striped64() { + } + + // 使用CAS更新base的值 + final boolean casBase(long cmp, long val) { + return UNSAFE.compareAndSwapLong(this, BASE, cmp, val); + } + + // 使用CAS将cells自旋标识更新为1 + // 更新为0时可以不用CAS,直接使用cellsBusy就行 + final boolean casCellsBusy() { + return UNSAFE.compareAndSwapInt(this, CELLSBUSY, 0, 1); + } + + // 下面这两个方法是ThreadLocalRandom中的方法,不过因为包访问关系,这里又重新写一遍 + + // probe翻译过来是探测/探测器/探针这些,不好理解,它是ThreadLocalRandom里面的一个属性, + // 不过并不影响对Striped64的理解,这里可以把它理解为线程本身的hash值 + static final int getProbe() { + return UNSAFE.getInt(Thread.currentThread(), PROBE); + } + + // 相当于rehash,重新算一遍线程的hash值 + static final int advanceProbe(int probe) { + probe ^= probe << 13; // xorshift + probe ^= probe >>> 17; + probe ^= probe << 5; + UNSAFE.putInt(Thread.currentThread(), PROBE, probe); + return probe; + } + + /** + * 核心方法的实现,此方法建议在外部进行一次CAS操作(cell != null时尝试CAS更新base值,cells != null时,CAS更新hash值取模后对应的cell.value) + * @param x the value 前面我说的二元运算中的第二个操作数,也就是外部提供的那个操作数 + * @param fn the update function, or null for add (this convention avoids the need for an extra field or function in LongAdder). + * 外部提供的二元算术操作,实例持有并且只能有一个,生命周期内保持不变,null代表LongAdder这种特殊但是最常用的情况,可以减少一次方法调用 + * @param wasUncontended false if CAS failed before call 如果为false,表明调用者预先调用的一次CAS操作都失败了 + */ + final void longAccumulate(long x, LongBinaryOperator fn, boolean wasUncontended) { + int h; + // 这个if相当于给线程生成一个非0的hash值 + if ((h = getProbe()) == 0) { + ThreadLocalRandom.current(); // force initialization + h = getProbe(); + wasUncontended = true; + } + boolean collide = false; // True if last slot nonempty 如果hash取模映射得到的Cell单元不是null,则为true,此值也可以看作是扩容意向,感觉这个更好理解 + for (;;) { + Cell[] as; Cell a; int n; long v; + if ((as = cells) != null && (n = as.length) > 0) { // cells已经被初始化了 + if ((a = as[(n - 1) & h]) == null) { // hash取模映射得到的Cell单元还为null(为null表示还没有被使用) + if (cellsBusy == 0) { // Try to attach new Cell 如果没有线程正在执行扩容 + Cell r = new Cell(x); // Optimistically create 先创建新的累积单元 + if (cellsBusy == 0 && casCellsBusy()) { // 尝试加锁 + boolean created = false; + 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; // 对没有使用的Cell单元进行累积操作(第一次赋值相当于是累积上一个操作数,求和时再和base执行一次运算就得到实际的结果) + created = true; + } + } finally { + cellsBusy = 0; 清空自旋标识,释放锁 + } + if (created) // 如果原本为null的Cell单元是由自己进行第一次累积操作,那么任务已经完成了,所以可以退出循环 + break; + continue; // Slot is now non-empty 不是自己进行第一次累积操作,重头再来 + } + } + collide = false; // 执行这一句是因为cells被加锁了,不能往下继续执行第一次的赋值操作(第一次累积),所以还不能考虑扩容 + } + else if (!wasUncontended) // CAS already known to fail 前面一次CAS更新a.value(进行一次累积)的尝试已经失败了,说明已经发生了线程竞争 + wasUncontended = true; // Continue after rehash 情况失败标识,后面去重新算一遍线程的hash值 + else if (a.cas(v = a.value, ((fn == null) ? v + x : fn.applyAsLong(v, x)))) // 尝试CAS更新a.value(进行一次累积) ------ 标记为分支A + break; // 成功了就完成了累积任务,退出循环 + else if (n >= NCPU || cells != as) // cell数组已经是最大的了,或者中途发生了扩容操作。因为NCPU不一定是2^n,所以这里用 >= + collide = false; // At max size or stale 长度n是递增的,执行到了这个分支,说明n >= NCPU会永远为true,下面两个else if就永远不会被执行了,也就永远不会再进行扩容 + // CPU能够并行的CAS操作的最大数量是它的核心数(CAS在x86中对应的指令是cmpxchg,多核需要通过锁缓存来保证整体原子性),当n >= NCPU时,再出现几个线程映射到同一个Cell导致CAS竞争的情况,那就真不关扩容的事了,完全是hash值的锅了 + else if (!collide) // 映射到的Cell单元不是null,并且尝试对它进行累积时,CAS竞争失败了,这时候把扩容意向设置为true + // 下一次循环如果还是跟这一次一样,说明竞争很严重,那么就真正扩容 + collide = true; // 把扩容意向设置为true,只有这里才会给collide赋值为true,也只有执行了这一句,才可能执行后面一个else if进行扩容 + else if (cellsBusy == 0 && casCellsBusy()) { // 最后再考虑扩容,能到这一步说明竞争很激烈,尝试加锁进行扩容 ------ 标记为分支B + try { + if (cells == as) { // Expand table unless stale 检查下是否被别的线程扩容了(CAS更新锁标识,处理不了ABA问题,这里再检查一遍) + Cell[] rs = new Cell[n << 1]; // 执行2倍扩容 + for (int i = 0; i < n; ++i) + rs[i] = as[i]; + cells = rs; + } + } finally { + cellsBusy = 0; // 释放锁 + } + collide = false; // 扩容意向为false + continue; // Retry with expanded table 扩容后重头再来 + } + h = advanceProbe(h); // 重新给线程生成一个hash值,降低hash冲突,减少映射到同一个Cell导致CAS竞争的情况 + } + else if (cellsBusy == 0 && cells == as && casCellsBusy()) { // cells没有被加锁,并且它没有被初始化,那么就尝试对它进行加锁,加锁成功进入这个else if + boolean init = false; + try { // Initialize table + if (cells == as) { // CAS避免不了ABA问题,这里再检测一次,如果还是null,或者空数组,那么就执行初始化 + Cell[] rs = new Cell[2]; // 初始化时只创建两个单元 + rs[h & 1] = new Cell(x); // 对其中一个单元进行累积操作,另一个不管,继续为null + cells = rs; + init = true; + } + } finally { + cellsBusy = 0; // 清空自旋标识,释放锁 + } + if (init) // 如果某个原本为null的Cell单元是由自己进行第一次累积操作,那么任务已经完成了,所以可以退出循环 + break; + } + else if (casBase(v = base, ((fn == null) ? v + x : fn.applyAsLong(v, x)))) // cells正在进行初始化时,尝试直接在base上进行累加操作 + break; // Fall back on using base 直接在base上进行累积操作成功了,任务完成,可以退出循环了 + } + } + + +``` + +这个类是jdk1.8新增的类,目的是为了提供一个通用的,更高效的用于并发场景的计数器 \ No newline at end of file diff --git a/week_02/23/Unsafe.md b/week_02/23/Unsafe.md new file mode 100644 index 0000000000000000000000000000000000000000..81177f08908684d1a4fccc3bed96631d39257319 --- /dev/null +++ b/week_02/23/Unsafe.md @@ -0,0 +1,161 @@ +## 获取实例 + +Unsafe 对象不能直接通过 **new Unsafe()** 或调用 **Unsafe.getUnsafe()** 获取。 + +- Unsafe 被设计成单例模式,构造方法私有。 +- getUnsafe 被设计成只能从引导类加载器(bootstrap class loader)加载。 + +```java +private Unsafe() { +} + +public static Unsafe getUnsafe() { + Class var0 = Reflection.getCallerClass(2); + if (var0.getClassLoader() != null) { + throw new SecurityException("Unsafe"); + } else { + return theUnsafe; + } +} +``` + +## 初始化代码 + +```java + private static native void registerNatives(); + static { + registerNatives(); + sun.reflect.Reflection.registerMethodsToFilter(Unsafe.class, "getUnsafe"); + } + + 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; + } +``` + +## 原子CAS操作 + + + +```java +/** + * Atomically exchanges the given reference value with the current + * reference value of a field or array element within the given + * object o at the given offset. + * + * @param o object/array to update the field/element in + * @param offset field/element offset + * @param newValue new value + * @return the previous value + * @since 1.8 + */ + public final Object getAndSetObject(Object o, long offset, Object newValue) { + Object v; + do { + v = getObjectVolatile(o, offset);//获取对象内存地址偏移量上的数值v + } while (!compareAndSwapObject(o, offset, v, newValue));//如果现在还是v,设置为newValue,否则返回false,!false=true,一直循环直到等于v退出循环返回v. + return v; + } +``` + +## 其他方法 + +```java +//扩充内存 + public native long reallocateMemory(long address, long bytes); + + //分配内存 + public native long allocateMemory(long bytes); + + //释放内存 + public native void freeMemory(long address); + + //在给定的内存块中设置值 + public native void setMemory(Object o, long offset, long bytes, byte value); + + //从一个内存块拷贝到另一个内存块 + public native void copyMemory(Object srcBase, long srcOffset, Object destBase, long destOffset, long bytes); + + //获取值,不管java的访问限制,其他有类似的getInt,getDouble,getLong,getChar等等 + public native Object getObject(Object o, long offset); + + //设置值,不管java的访问限制,其他有类似的putInt,putDouble,putLong,putChar等等 + public native void putObject(Object o, long offset); + + //从一个给定的内存地址获取本地指针,如果不是allocateMemory方法的,结果将不确定 + public native long getAddress(long address); + + //存储一个本地指针到一个给定的内存地址,如果地址不是allocateMemory方法的,结果将不确定 + public native void putAddress(long address, long x); + + //该方法返回给定field的内存地址偏移量,这个值对于给定的filed是唯一的且是固定不变的 + public native long staticFieldOffset(Field f); + + //报告一个给定的字段的位置,不管这个字段是private,public还是保护类型,和staticFieldBase结合使用 + public native long objectFieldOffset(Field f); + + //获取一个给定字段的位置 + public native Object staticFieldBase(Field f); + + //确保给定class被初始化,这往往需要结合基类的静态域(field) + public native void ensureClassInitialized(Class c); + + //可以获取数组第一个元素的偏移地址 + public native int arrayBaseOffset(Class arrayClass); + + //可以获取数组的转换因子,也就是数组中元素的增量地址。将arrayBaseOffset与arrayIndexScale配合使用, 可以定位数组中每个元素在内存中的位置 + public native int arrayIndexScale(Class arrayClass); + + //获取本机内存的页数,这个值永远都是2的幂次方 + public native int pageSize(); + + //告诉虚拟机定义了一个没有安全检查的类,默认情况下这个类加载器和保护域来着调用者类 + public native Class defineClass(String name, byte[] b, int off, int len, ClassLoader loader, ProtectionDomain protectionDomain); + + //定义一个类,但是不让它知道类加载器和系统字典 + public native Class defineAnonymousClass(Class hostClass, byte[] data, Object[] cpPatches); + + //锁定对象,必须是没有被锁的 + public native void monitorEnter(Object o); + + //解锁对象 + public native void monitorExit(Object o); + + //试图锁定对象,返回true或false是否锁定成功,如果锁定,必须用monitorExit解锁 + public native boolean tryMonitorEnter(Object o); + + //引发异常,没有通知 + public native void throwException(Throwable ee); + + //CAS,如果对象偏移量上的值=期待值,更新为x,返回true.否则false.类似的有compareAndSwapInt,compareAndSwapLong,compareAndSwapBoolean,compareAndSwapChar等等。 + public final native boolean compareAndSwapObject(Object o, long offset, Object expected, Object x); + + // 该方法获取对象中offset偏移地址对应的整型field的值,支持volatile load语义。类似的方法有getIntVolatile,getBooleanVolatile等等 + public native Object getObjectVolatile(Object o, long offset); + + //线程调用该方法,线程将一直阻塞直到超时,或者是中断条件出现。 + public native void park(boolean isAbsolute, long time); + + //终止挂起的线程,恢复正常.java.util.concurrent包中挂起操作都是在LockSupport类实现的,也正是使用这两个方法 + public native void unpark(Object thread); + + //获取系统在不同时间系统的负载情况 + public native int getLoadAverage(double[] loadavg, int nelems); + + //创建一个类的实例,不需要调用它的构造函数、初使化代码、各种JVM安全检查以及其它的一些底层的东西。即使构造函数是私有,我们也可以通过这个方法创建它的实例,对于单例模式,简直是噩梦,哈哈 + public native Object allocateInstance(Class cls) throws InstantiationException; +``` + +BootstrapClassLoader加载${JAVA_HOME}/jre/lib 下面的部分jar包。比如java.、sun. +ExtClassLoader加载${JAVA_HOME}/jre/lib/ext下面的jar包。比如javax.* +AppClassLoader加载用户classpath下面的jar包。 +如果自定义了classloader, 在符合双亲委派模型的基础上,它加载用户自定义classpath下的jar包, 例如tomcat的WEB-INF/class和WEB-INF/lib. +