【Java 基础 12】Atomic

java.util.concurrent.Atomic包下定义了很多具有原子操作的类


先看看Atomic包下常用的AtomicInteger类

package java.util.concurrent.atomic;

import java.lang.invoke.VarHandle;
import java.util.function.IntBinaryOperator;
import java.util.function.IntUnaryOperator;


public class AtomicInteger extends Number implements java.io.Serializable {
    private static final long serialVersionUID = 6214790243416807050L;

    // JDK私有的Unsafe类,可以操作系统内存,提供一些基于CAS实现的原子操作方法,保证value线程安全
    private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
    // 通过Unsafe实例U获取value字段在内存地址的偏移量
    private static final long VALUE = U.objectFieldOffset(AtomicInteger.class, "value");

    // AtomicInteger维护的int value, 被volatile修饰,保证线程可见和有序性,私有的不能直接获取,需要通过get()方法获取
    private volatile int value;


    // 构造函数,设置初始值
    public AtomicInteger(int initialValue) {
        value = initialValue;
    }

    // 若不指定初始值,则默认0
    public AtomicInteger() {
    }

    // 获取value
    public final int get() {
        return value;
    }

    // 给value赋值,由于volatile保证可见性和有序性,对value赋值会插入内存屏障,强制将新数据立即刷入内存
    public final void set(int newValue) {
        value = newValue;
    }

    // 直接获取内存中value值
    public final int getPlain() {
        return U.getInt(this, VALUE);
    }

    // 直接设置内存中value值
    public final void setPlain(int newValue) {
        U.putInt(this, VALUE, newValue);
    }


    // lazySet方法则是通过Unsafe实例调用putIntRelease进而调用putIntVolatile方法直接在value字段所在内存地址上去赋值,没有使用内存屏障,提高程序的执行效率,但是不能保证可见性(没有从工作线程刷新到内存这个操作,总线嗅探不到,其他线程仍然可能操作自己的工作线程中的数据)
    public final void lazySet(int newValue) {
        U.putIntRelease(this, VALUE, newValue);
    }

    // 以原子方式设置为给定值,并返回旧值
    public final int getAndSet(int newValue) {
        return U.getAndSetInt(this, VALUE, newValue);
    }

    // 在内存上,CAS设置value
    public final boolean compareAndSet(int expectedValue, int newValue) {
        return U.compareAndSetInt(this, VALUE, expectedValue, newValue);
    }
    // 方法过期,建议用weakCompareAndSetPlain
    @Deprecated(since="9")
    public final boolean weakCompareAndSet(int expectedValue, int newValue) {
        return U.weakCompareAndSetIntPlain(this, VALUE, expectedValue, newValue);
    }

    // weakCompareAndSetPlain也是CAS但是移除了volatile变量操作前后的内存屏障,因此不能保证有序性
    public final boolean weakCompareAndSetPlain(int expectedValue, int newValue) {
        return U.weakCompareAndSetIntPlain(this, VALUE, expectedValue, newValue);
    }


    // 以原子方式将当前值加 1,并返回旧值
    public final int getAndIncrement() {
        return U.getAndAddInt(this, VALUE, 1);
    }
    // 以原子方式将当前值加 1,并返回新值
    public final int incrementAndGet() {
        return U.getAndAddInt(this, VALUE, 1) + 1;
    }


    // 以原子方式将当前值减 1,并返回旧值
    public final int getAndDecrement() {
        return U.getAndAddInt(this, VALUE, -1);
    }
    // 以原子方式将当前值减 1,并返回新值
    public final int decrementAndGet() {
        return U.getAndAddInt(this, VALUE, -1) - 1;
    }


    // 以原子方式将当前值增加指定值delta,并返回旧值
    public final int getAndAdd(int delta) {
        return U.getAndAddInt(this, VALUE, delta);
    }
    // 以原子方式将当前值增加指定值delta,并返回新值
    public final int addAndGet(int delta) {
        return U.getAndAddInt(this, VALUE, delta) + delta;
    }


    // 使用IntUnaryOperator接口的方法修改value,并返回旧值
    public final int getAndUpdate(IntUnaryOperator updateFunction) {
        int prev = get(), next = 0;
        for (boolean haveNext = false;;) {
            if (!haveNext)
                // updateFunction接口的抽象applyAsInt方法,可以在主程序中通过该接口的内部类对象实现,计算得到新的值,通过weakCompareAndSetVolatile修改内存中的value
                next = updateFunction.applyAsInt(prev);
            if (weakCompareAndSetVolatile(prev, next))
                return prev;  //返回旧值
            haveNext = (prev == (prev = get()));
        }
    }

    // 使用IntBinaryOperator 接口的applyAsInt方法修改value, 并返回旧值,和getAndUpdate不同的是IntBinaryOperator接口的applyAsInt方法有两个参数
    public final int getAndAccumulate(int x,
                                      IntBinaryOperator accumulatorFunction) {
        int prev = get(), next = 0;
        for (boolean haveNext = false;;) {
            if (!haveNext)
                next = accumulatorFunction.applyAsInt(prev, x);
            if (weakCompareAndSetVolatile(prev, next))
                return prev;
            haveNext = (prev == (prev = get()));
        }
    }


     // 使用IntUnaryOperator接口的方法修改value,并返回新值
    public final int updateAndGet(IntUnaryOperator updateFunction) {
        int prev = get(), next = 0;
        for (boolean haveNext = false;;) {
            if (!haveNext)
                next = updateFunction.applyAsInt(prev);
            if (weakCompareAndSetVolatile(prev, next))
                return next;
            haveNext = (prev == (prev = get()));
        }
    }


    // 使用IntBinaryOperator 接口的applyAsInt方法修改value, 并返回新值,和updateAndGet不同的是IntBinaryOperator接口的applyAsInt方法有两个参数
    public final int accumulateAndGet(int x,
                                      IntBinaryOperator accumulatorFunction) {
        int prev = get(), next = 0;
        for (boolean haveNext = false;;) {
            if (!haveNext)
                next = accumulatorFunction.applyAsInt(prev, x);
            if (weakCompareAndSetVolatile(prev, next))
                return next;
            haveNext = (prev == (prev = get()));
        }
    }
  
    // ...

}

重点私有属性

1. private volatile int value;    // AtomicInteger维护的int value, 被volatile修饰,保证线程可见和有序性;   

2. private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();  //JDK私有的Unsafe类,可以操作系统内存,提供一些基于CAS实现的原子操作方法,保证value操作的原子性;

3. private static final long VALUE = U.objectFieldOffset(AtomicInteger.class, "value");    // 通过Unsafe实例U获取value字段在内存地址的偏移量,可以勇敢Unsafe实例U直接操作内存上的value;

重点实现方法

方法方法说明
public final int get();
获取value的值                                                                      
public final void set(int newValue);
设置value的值,volatile保证可见性和有序性,也单独的写操作的原子性
public final void lazySet(int newValue);
 lazySet方法则是通过Unsafe实例调用putIntRelease进而调用putIntVolatile方法直接在value字段所在内存地址上去赋值,没有使用内存屏障,提高程序的执行效率,但是不能保证可见性(没有从工作线程刷新到内存这个操作,总线嗅探不到,其他线程仍然可能操作自己的工作线程中的数据)
public final int getPlain();
通过Unsafe实例和value字段在内存地址的偏移量VALUE直接获取内存上的value
public final void setPlain(int newValue);
通过Unsafe实例调用putInt()方法直接修改内存上的value
public final int getAndSet(int newValue);
以原子方式设置为给定值,并返回旧值
public final boolean compareAndSet(int expectedValue, int newValue);
直接在内存上通过CAS修改value
public final boolean weakCompareAndSetPlain(int expectedValue, int newValue);
weakCompareAndSetPlain也是CAS但是移除了volatile变量操作前后的内存屏障,因此不能保证有序性
public final int getAndIncrement();
原子自增1,并返回旧值
public final int incrementAndGet();
原子自增1,并返回新值
public final int getAndDecrement();
原子自减1,并返回旧值
public final int decrementAndGet();
原子自减1,并返回新值
public final int getAndAdd(int delta);
原子自增delta,并返回旧值
public final int addAndGet(int delta);
原子自增delta,并返回新值
public final int getAndUpdate(IntUnaryOperator updateFunction);
实现IntUnaryOperator接口的方法修改value,并返回旧值
public final int updateAndGet(IntUnaryOperator updateFunction);
实现IntUnaryOperator接口的方法修改value,并返回新值
public final int getAndAccumulate(int x,
                                  IntBinaryOperator accumulatorFunction);
实现IntBinaryOperator接口的方法修改value,并返回旧值
public final int accumulateAndGet(int x,
                                  IntBinaryOperator accumulatorFunction);
实现IntBinaryOperator接口的方法修改value,并返回新值

自增自减的几个方法都是通过Unsafe实例调用getAndAddInt方法修改value:

@HotSpotIntrinsicCandidate
public final int getAndAddInt(Object o, long offset, int delta) {
    int v;
    // 自旋
    do {
        // 获取内存上的value
        v = getIntVolatile(o, offset);
    // CAS
    } while (!weakCompareAndSetInt(o, offset, v, v + delta));
    // 返回旧值
    return v;
}

AtomicInteger优点

1. 乐观锁,通过CAS保证读写操作的原子性,保证线程安全;

AtomicInteger缺点

1. 自旋,占CPU资源;

2. 存在CAS的ABA问题。


AtomicLong和AtomicInteger的原理一致,都是通过Unsafe类和CAS保证value线程安全

AtomicBoolean内部则是使用VarHandle代替Unsafe, VarHandle主要是提供Atomic和Unsafe相似的功能,但会更加安全和易用,并且在并发方面提高了性能。在Java并发包中,官方现在已不推荐再使用Unsafe这个类了,例如在AtomicIntegerArray,AtomicLongArray,AtomicReference和AtomicReferenceArray中已经用VarHandle替换了Unsafe,并且在CAS中也会用到。

public class AtomicIntegerArray implements java.io.Serializable {

    // VarHandle 替换Unsafe类,来实现array的线程安全
    private static final VarHandle AA
        = MethodHandles.arrayElementVarHandle(int[].class);

    // AtomicIntegerArray维护的是一个int数组
    private final int[] array;

    // ...
}

解决CAS的ABA问题的Atomic类:AtomicStampedReference和AtomicMarkableReference

public class AtomicStampedReference<V> {

    // Pair<T> 给reference绑定一个stamp变量,用stamp来表示reference被修改的次数,相当于记录reference的版本号,线程在比较reference预期值和内存值相同后还需要比较stamp相同,才能进行修改
    private static class Pair<T> {
        final T reference;
        final int stamp;
        private Pair(T reference, int stamp) {
            this.reference = reference;
            this.stamp = stamp;
        }
        static <T> Pair<T> of(T reference, int stamp) {
            return new Pair<T>(reference, stamp);
        }
    }

    // ...
}
public class AtomicMarkableReference<V> {

    // Pair<T> 给reference绑定一个mark变量,用mark来标记在拿到reference和比较内存值期间reference是否被修改过,线程在比较reference预期值和内存值相同后还需要比较mark为false,才能进行修改
    private static class Pair<T> {
        final T reference;
        final boolean mark;
        private Pair(T reference, boolean mark) {
            this.reference = reference;
            this.mark = mark;
        }
        static <T> Pair<T> of(T reference, boolean mark) {
            return new Pair<T>(reference, mark);
        }
    }

}

再来看看Atomic包下一个抽象类:Striped64

package java.util.concurrent.atomic;

import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.util.Arrays;
import java.util.concurrent.ThreadLocalRandom;
import java.util.function.DoubleBinaryOperator;
import java.util.function.LongBinaryOperator;


@SuppressWarnings("serial")
abstract class Striped64 extends Number {
    
  
    @jdk.internal.vm.annotation.Contended static final class Cell {
        // 保存要累加的值
        volatile long value;
        Cell(long x) { value = x; }

        // VALUE是VarHandle类的实例,调用compareAndSet方法,CAS更新value
        final boolean cas(long cmp, long val) {
            return VALUE.compareAndSet(this, cmp, val);
        }
        final void reset() {
            VALUE.setVolatile(this, 0L);
        }
        final void reset(long identity) {
            VALUE.setVolatile(this, identity);
        }
        final long getAndSet(long val) {
            return (long)VALUE.getAndSet(this, val);
        }

        private static final VarHandle VALUE;
        static {
            try {
                MethodHandles.Lookup l = MethodHandles.lookup();
                VALUE = l.findVarHandle(Cell.class, "value", long.class);
            } catch (ReflectiveOperationException e) {
                throw new ExceptionInInitializerError(e);
            }
        }
    }

    // cpu数量, 主要在cell数组扩容中参考使用
    static final int NCPU = Runtime.getRuntime().availableProcessors();

    // cell表,容量为2的次幂
    transient volatile Cell[] cells;

    // 基础值,在更新操作时基于CAS无锁技术实现原子更新
    transient volatile long base;

    // 自旋锁  用于保护创建或者扩展Cell表
    transient volatile int cellsBusy;


    // 获取base成员变量相对于Striped64对象头的偏移地址BASE,cellsBusy这个自旋锁相对于Striped64对象头的偏移地址CELLSBUSY,和threadLocalRandomProbe相对于Thread类对象头的偏移地址THREAD_PROBE
    private static final VarHandle BASE;
    private static final VarHandle CELLSBUSY;
    private static final VarHandle THREAD_PROBE;
    static {
        try {
            MethodHandles.Lookup l = MethodHandles.lookup();
            BASE = l.findVarHandle(Striped64.class,
                    "base", long.class);
            CELLSBUSY = l.findVarHandle(Striped64.class,
                    "cellsBusy", int.class);
            l = java.security.AccessController.doPrivileged(
                    new java.security.PrivilegedAction<>() {
                        public MethodHandles.Lookup run() {
                            try {
                                return MethodHandles.privateLookupIn(Thread.class, MethodHandles.lookup());
                            } catch (ReflectiveOperationException e) {
                                throw new ExceptionInInitializerError(e);
                            }
                        }});
            THREAD_PROBE = l.findVarHandle(Thread.class,
                    "threadLocalRandomProbe", int.class);
        } catch (ReflectiveOperationException e) {
            throw new ExceptionInInitializerError(e);
        }
    }




    Striped64() {
    }


    //cas更新base
    final boolean casBase(long cmp, long val) {
        return BASE.compareAndSet(this, cmp, val);
    }

    // cas更新base并返回旧值
    final long getAndSetBase(long val) {
        return (long)BASE.getAndSet(this, val);
    }

    // 对cellsBusy(自旋锁)成员变量进行更新,如果cellsBusy为0(自旋锁未被占用状态)则将cellsBusy置为1(占用状态)并返回true.如果cellsBusy为1(占用状态),获取自旋锁失败,不进行任何操作,返回false.
    final boolean casCellsBusy() {
        return CELLSBUSY.compareAndSet(this, 0, 1);
    }

    // 获取当前线程的probe(hash)值
    static final int getProbe() {
        return (int) THREAD_PROBE.get(Thread.currentThread());
    }

    // 通过移位和异或运算更新当前线程的probe值(hash值)并返回
    static final int advanceProbe(int probe) {
        probe ^= probe << 13;   
        probe ^= probe >>> 17;
        probe ^= probe << 5;
        THREAD_PROBE.set(Thread.currentThread(), probe);
        return probe;
    }

    // X:累加值, fn:LongBinaryOperator接口,需要程序员实现接口中的applyAsLong方法, wasUncontended:是否存在竞争,如果CAS失败后wasUncontended为false
    final void longAccumulate(long x, LongBinaryOperator fn,
                              boolean wasUncontended) {
        int h;
        if ((h = getProbe()) == 0) {
            ThreadLocalRandom.current(); 
            h = getProbe();
            wasUncontended = true;
        }
        // 标记是否碰撞,用于控制扩容
        boolean collide = false; 
        // 设置标签done            
        done: 
        for (;;) {
            Cell[] cs; Cell c; int n; long v;
            // cs=cells并判断cs是否非空且有元素
            if ((cs = cells) != null && (n = cs.length) > 0) {
             //计算索引:线程probe & (cells数组长度-1),获取cells数组索引处的元素判断是否为空
                if ((c = cs[(n - 1) & h]) == null) {
                    // 判断自旋锁是否已经未被占用
                    if (cellsBusy == 0) { 
                        // 新建cell对象r并初始化value=x      
                        Cell r = new Cell(x);  
                        // 双重检查是否锁未被占用,并获取锁(将cellsBusy修改为1) 
                        if (cellsBusy == 0 && casCellsBusy()) {
                            try {           
                                Cell[] rs; int m, j;
                                // 双重检查 判断cells数组是否非空且有元素且cells数组索引处的元素判断是否为空 
                                if ((rs = cells) != null &&
                                    (m = rs.length) > 0 &&
                                    rs[j = (m - 1) & h] == null) {
                                    // 在该索引处存储r,并跳到done处
                                    rs[j] = r;
                                    break done;
                                }
                            } finally {
                                // finally域中保证锁被释放
                                cellsBusy = 0;
                            }
                            continue;          
                        }
                    }
                    collide = false;
                }
                else if (!wasUncontended)       
                    wasUncontended = true;
                else if (c.cas(v = c.value,(fn == null) ? v + x : fn.applyAsLong(v, x)))
                    break;
                // 判断表大小是否达到上限或已经进行扩容      
                else if (n >= NCPU || cells != cs)
                    collide = false;            
                else if (!collide)
                    collide = true;
                // 锁未占用则获取锁
                else if (cellsBusy == 0 && casCellsBusy()) {
                    try {
                        if (cells == cs)  
                            // 2倍扩容      
                            cells = Arrays.copyOf(cs, n << 1);
                    } finally {
                        // 保证锁被释放
                        cellsBusy = 0;
                    }
                    collide = false;
                    continue;                   
                }
                // 更新hash值,在下一次循环中继续计算探索新的索引 h & (length-1)
                h = advanceProbe(h);
            }
            // 如果cells为null或者没有元素, 判断锁是否被占用, 如果没有占用,
            // 判断cells等于cs(新建的cells[],还未初始化),并是否成功获取锁
            else if (cellsBusy == 0 && cells == cs && casCellsBusy()) {
                try {                          
                    if (cells == cs) {
                        // cells初始容量为2
                        Cell[] rs = new Cell[2];
                        // 新建cell对象初始化value=x,存储在线程probe & (2-1)索引处,将rs赋给cells并跳转到done处
                        rs[h & 1] = new Cell(x);
                        cells = rs;
                        break done;
                    }
                } finally {
                    // 保证锁能够被释放
                    cellsBusy = 0;
                }
            }
            // 如果cells为null或者无元素,自旋锁又被占用或者无法成功获取锁
            // 使用fn的applyAsLong方法计算新的value值,调用casBase更新base,如果更新不成功,则自旋
            else if (casBase(v = base, (fn == null) ? v + x : fn.applyAsLong(v, x)))
                break done;
        }
    }

    // 和longAccumulate原理类似
    final void doubleAccumulate(double x, DoubleBinaryOperator fn,
                                boolean wasUncontended) {
        int h;
        if ((h = getProbe()) == 0) {
            ThreadLocalRandom.current(); 
            h = getProbe();
            wasUncontended = true;
        }
        boolean collide = false;                
        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) {       
                        Cell r = new Cell(Double.doubleToRawLongBits(x));
                        if (cellsBusy == 0 && casCellsBusy()) {
                            try {               
                                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;           
                        }
                    }
                    collide = false;
                }
                else if (!wasUncontended)      
                    wasUncontended = true;      
                else if (c.cas(v = c.value, apply(fn, v, x)))
                    break;
                else if (n >= NCPU || cells != cs)
                    collide = false;            
                else if (!collide)
                    collide = true;
                else if (cellsBusy == 0 && casCellsBusy()) {
                    try {
                        if (cells == cs)        
                            cells = Arrays.copyOf(cs, n << 1);
                    } finally {
                        cellsBusy = 0;
                    }
                    collide = false;
                    continue;                  
                }
                h = advanceProbe(h);
            }
            else if (cellsBusy == 0 && cells == cs && casCellsBusy()) {
                try {                        
                    if (cells == cs) {
                        Cell[] rs = new Cell[2];
                        rs[h & 1] = new Cell(Double.doubleToRawLongBits(x));
                        cells = rs;
                        break done;
                    }
                } finally {
                    cellsBusy = 0;
                }
            }
            else if (casBase(v = base, apply(fn, v, x)))
                break done;
        }
    }

    // 因为cell对象中的value是long类型的, 先对v强转为double型, 然后调用DoubleBinaryOperator接口的applyAsDouble方法(需要程序员实现)计算新的值并返回
    private static long apply(DoubleBinaryOperator fn, long v, double x) {
        double d = Double.longBitsToDouble(v);
        d = (fn == null) ? d + x : fn.applyAsDouble(d, x);
        return Double.doubleToRawLongBits(d);
    }

}

striped64是java8用来并发计数新加的组件,在单线程或者没有线程竞争的情况下,直接对base操作,当遇到多线程的时候,计算索引= 线程的probe(hash值) & (cells数组长度-1),找到对应的Cell, cas改变Cell中的值,最后的所求的结果就是base+所有Cell中的值,因此执行效率高。

64指的是计数64bit的数,即Long类型的数和Double类型的数。striped64是个抽象类,它的实现类有LongAdder,LongAccumulator, DoubleAdder, DoubleAccumulator(LongAccumulator和DoubleAccumulator各自在LongAdder和DoubleAdder的基础上,分别通过LongBinaryOperator和DoubleBinaryOperator接口,指定计算新的value的方法)

public class LongAdder extends Striped64 implements Serializable {
    private static final long serialVersionUID = 7249069246863182397L;

    public LongAdder(){}

    public void add(long x) {
        Cell[] cs; long b, v; int m; Cell c;

        // 判断是cells是否非null,如果非null,接着CAS更新base并判断是否更新成功
        if ((cs = cells) != null || !casBase(b = base, b + x)) {
            boolean uncontended = true;
            // 双重检查cells是否为空,或者没有元素, 或者有元素但是线程对应索引处cell为null,或者更新value失败, 则调用longAccumulate方法探测位置并添加x
            if (cs == null || (m = cs.length - 1) < 0 ||
                (c = cs[getProbe() & m]) == null ||
                !(uncontended = c.cas(v = c.value, v + x)))
                longAccumulate(x, null, uncontended);
        }
    }

    public void increment() {
        add(1L);
    }

    public void decrement() {
        add(-1L);
    }

    // 获取所有cell的和
    public long sum() {
        Cell[] cs = cells;
        long sum = base;
        if (cs != null) {
            for (Cell c : cs)
                if (c != null)
                    sum += c.value;
        }
        return sum;
    }

    // cells全部清零
    public void reset() {
        Cell[] cs = cells;
        base = 0L;
        if (cs != null) {
            for (Cell c : cs)
                if (c != null)
                    c.reset();
        }
    }

    // 计算cells的和并全部清零
    public long sumThenReset() {
        Cell[] cs = cells;
        long sum = getAndSetBase(0L);
        if (cs != null) {
            for (Cell c : cs) {
                if (c != null)
                    // 更新为0L并返回旧值
                    sum += c.getAndSet(0L);  
            }
        }
        return sum;
    }

    private static class SerializationProxy implements Serializable {
        private static final long serialVersionUID = 7249069246863182397L;

        private final long value;

        SerializationProxy(LongAdder a) {
            value = a.sum();
        }

        // 指定反序列化解析得到的对象
        private Object readResolve() {
            LongAdder a = new LongAdder();
            a.base = value;
            return a;
        }
    }

    private Object writeReplace() {
        return new SerializationProxy(this);
    }

    private void readObject(java.io.ObjectInputStream s)
        throws java.io.InvalidObjectException {
        throw new java.io.InvalidObjectException("Proxy required");
    }

}


 

对象被访问的时候是怎么被找到的?

创建一个对象的时候,在栈内存中会有一个引用变量,指向堆内存中该对象实例,访问该实例有两种方法:

句柄访问

JVM的堆内存中划分出一块内存来作为句柄池,引用变量中存储的就是对象的句柄地址,句柄中包含了对象实例数据与类型数据各自的具体地址信息。在内存垃圾收集之后,对象会移动,句柄访问不直接,访问速度较慢。

直接指针访问

Unsafe,VarHandle类就是获取对象在内存地址的偏移量得到对象的直接指针,直接指针的访问方式节省了一次指针定位的时间开销,速度较快。

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值