java中在Unsafe中提供了三种CAS *** 作:
compareAndSwapInt(),compareAndSwapObject(),compareAndSwapLong()
//参数含义:对象,属性的内存偏移量,属性期望值,属性更新值 public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5); public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5); public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);
偏移量:内存中的对象包含对象头和对象实例数据,而对象头占8个字节,对于64位 *** 作系统的压缩指针占4个字节, 所以我们一般说对象头占12个字节;例如对于一个test对象,x偏移量为这个对象的对象头即12个字节,y的偏移量为16
cas *** 作修改Test类的x变量。
public class CASTest { public static void main(String[] args) { Test test = new Test(); Unsafe unsafe = UnsafeFactory.getUnsafe(); long xOffset = UnsafeFactory.getFieldOffset(unsafe, Test.class, "x"); System.out.println(xOffset); //12 long yOffset = UnsafeFactory.getFieldOffset(unsafe, Test.class, "y"); System.out.println(yOffset); //16 unsafe.compareAndSwapInt(test, xOffset, 0, 1); System.out.println(test.x); } static class Test { int x; int y; } }
CAS可以保证原子性,但是不能保证有序性和可见性,所以一般地,CAS配合volatile使用可以保证线程安全。底层最终通过执行一个cas指令(原子 *** 作修改变量值),通过期望值和内存中的实际值进行比较,比较的结果如果相等则返回的是旧值(期望值)表示CAS *** 作成功,不相等则返回内存中实际值表示CAS *** 作失败。
CAS实现线程安全的 *** 作
public class CASTest { private static int sum = 0; private static CASLock casLock = new CASLock(); public static void main(String[] args) throws InterruptedException { for (int i=0; i<10; i++) { new Thread(() -> { for (;;) { if (casLock.getState() == 0 && casLock.cas()) { try { for (int j = 0; j < 10000; j++) { sum++; } } finally { casLock.setState(0); } break; } } }).start(); } Thread.sleep(2000); System.out.println(sum); } }
public class CASLock { private volatile int state = 0; private static final Unsafe UNSAFE; private static final long OFFSET; static { UNSAFE = UnsafeFactory.getUnsafe(); OFFSET = UnsafeFactory.getFieldOffset(UNSAFE, CASLock.class, "state"); } public int getState() { return state; } public void setState(int state) { this.state = state; } public boolean cas() { return UNSAFE.compareAndSwapInt(this, OFFSET, 0, 1); } }
jdk中juc包下原子类都是通过cas实现线程安全的。
ABA的问题可以通过版本号来解决
LongAdder、DoubleAdder原理
在高并发下,CAS *** 作会有大量的线程自旋,导致浪费线程资源,而为了提高执行效率,将V值拆分为多个变量,多个线程同时对各自的变量进行cas *** 作,所有线程执行完成后,将所有变量进行累加统计。其思想与jdk8中ConcurrentHashMap统计元素个数类似,LongAdder、DoubleAdder也实现了该思想。LongAdder中定义了base变量和cell数组变量,通过hash对Cell数组初始化和累加 *** 作,最后将base和Cell数组所有的数进行累加得到结果。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)