Java中的sun.misc.Unsafe compareAndSwap putByte

从字面意思就可以看出, 这些接口在java世界, 是不安全的, 都是一些native (jni) 接口, 会直接修改内存中的值, 一旦指针弄错了, 结果是令人崩溃的.

本文只介绍几个常用的接口.

compareAndSwapInt

原型
/**
* Atomically update Java variable to x if it is currently
* holding expected.
* @return true if successful
*/
public final native boolean compareAndSwapInt(Object o, long offset, int expected, int x);
接口含义:
首先找出Object o在内存中的位置p, 然后偏移offset个字节, 设p+offset处的这个int值为y,
如果y == expected, 则执行赋值操作y = x, 返回true
如果y != expected, 则不执行赋值操作, 返回false
使用例子

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import sun.misc.Unsafe;
import java.lang.reflect.Field;
public class UnsafeExample {
    private final static Logger logger = LogManager.getLogger(UnsafeExample.class);
    static Unsafe U;
    int x = 2;

    static {
        Field f = null;
        try {
          /* 我所使用的jdk8上是theUnsafe */
            f = Unsafe.class.getDeclaredField("theUnsafe");
            f.setAccessible(true);
            U = (Unsafe) f.get(null);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws Throwable{
        long offset = U.objectFieldOffset(UnsafeExample.class.getDeclaredField("x"));

        UnsafeExample example = new UnsafeExample();
        boolean ret;
        //如果x==2, 则执行x=3, 返回true
        ret = U.compareAndSwapInt(example, offset, 2, 3);
        logger.info("offset: {}, ret: {}, x: {}", offset, ret, example.x);

        //如果x==4, 则执行x=5.  实际上x==3, 不成立, 赋值失败, 返回false
        ret = U.compareAndSwapInt(example, offset, 4, 5);
        logger.info("offset: {}, ret: {}, x: {}", offset, ret, example.x);
    }
}

打印

2019-02-07 17:47:47.859 INFO  main [UnsafeExample.java:34] offset: 12, ret: true, x: 3
2019-02-07 17:47:47.859 INFO  main [UnsafeExample.java:36] offset: 12, ret: false, x: 3

compareAndSwapObject compareAndSwapLong
与compareAndSwapInt类似, 不多说了

compareAndSwap底层原理

compareAndSwap简称CAS, 以Intel CPU为例, 它有个指令叫lock, 它可禁止不同的处理器同时操作CPU之间的共享内存.
以下是汇编举例

lock cmpxchg dword ptr [edx], ecx

解释
dword ptr表示32bit指针
[edx]为寄存器间接寻址
整句话的含义是:
eax中的值与[edx]中的值比较,
如果相同, 则将ecx中的值赋给[edx], 并且ZF1
如果不同, 则将[edx]中的值赋给eax. 并且ZF0
整个操作是原子的

getAndAddInt

原型 public final int getAndAddInt(Object o, long offset, int delta)
与compareAndSwapInt相似, 将对象偏移offset地址的值加上delta, 源代码如下

    public final int getAndAddInt(Object o, long offset, int delta) {
        int v;
        do {
            v = getIntVolatile(o, offset);
        } while (!compareAndSwapInt(o, offset, v, v + delta));
        return v;
    }

getAndAddLong也是类似的.

    public final long getAndAddLong(Object o, long offset, long delta) {
        long v;
        do {
            v = getLongVolatile(o, offset);
        } while (!compareAndSwapLong(o, offset, v, v + delta));
        return v;
    }

loadFence/storeFence

我也不懂这两个接口如何使用. 从底层的C++实现来看, 它影响的是对变量的读/写操作.
比如线程1修改变量X, 然后线程2读取变量X的值, 那么线程2读到的到底是新值, 还是旧值?
有点类似volatile的作用

put/get操作

例如putByte, putShort等
将对象偏移offset后的地址的值修改为另一个值. 以修改数组举例

public class PutExample {
    private final static Logger logger = LogManager.getLogger(PutExample.class);
    static Unsafe U;
    static {
        Field f = null;
        try {
            f = Unsafe.class.getDeclaredField("theUnsafe");
            f.setAccessible(true);
            U = (Unsafe) f.get(null);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws Throwable {
        int offset = U.arrayBaseOffset(byte[].class);
        final byte[] bytes = {1, 2, 3};
        logger.info("offset: {}, src: {}", offset, bytes);
        U.putByte(bytes, offset + 1L, (byte) 10);
        logger.info("modified: {}", bytes);
    }
}

执行结果为

2019-02-07 20:15:01.719 INFO  main [PutExample.java:27] offset: 16, src: [1, 2, 3]
2019-02-07 20:15:01.719 INFO  main [PutExample.java:29] modified: [1, 10, 3]

参考

JDK8 https://download.java.net/openjdk/jdk8/
Intel指令集 http://jsimlo.sk/docs/cpu/index.php/cmpxchg.html
http://jsimlo.sk/docs/cpu/index.php/lock.html

猜你喜欢

转载自blog.csdn.net/wzj_whut/article/details/86772268