Unsafe使用初探

在jdk源码中,经常能够看到sun.misc.Unsafe的使用,通过Unsafe可以操作内存管理等相关操作。
1.怎么使用Unsafe?

 

public final class Unsafe{
private static final Unsafe theUnsafe;
private Unsafe(){}
public static Unsafe getUnsafe(){
     Class cc = sun.reflect.Reflection.getCallerClass(2);
       if (cc.getClassLoader() != null)
          throw new SecurityException("Unsafe");
       return theUnsafe;
}
}

 从Unsafe类中可以看出该类并不推荐让外部用户来使用,首先构造函数为私有的,外部无法通过new实例来使用该类,其次静态的getUnsafe方法的使用也有局限,类加载器必须是bootstrap时,才会返回对应实例。

 

那该怎么办?还好可以通过反射来获取Unsafe的实例,代码如下:

 

 Field field = Unsafe.class.getDeclaredField("theUnsafe");
 field.setAccessible(true);
 Unsafe unsafe = (Unsafe)field.get(null);

 2.获取到unsafe实例后,下面主要介绍常用的功能

 

(1)修改对象的属性值

      这个在开发过程中经常会用到,通常的做法是通过反射来进行设置,现在使用unsafe也可以做到这一点。具体操作用到unsafe的objectFieldOffset方法,它返回成员属性在内存中的地址相对于对象内存地址的偏移量。通过该方法可以计算一个对象在内存中的空间大小,找出Field中偏移量最大值,然后对该最大偏移值填充字节数即为对象大小。下面比较反射以及unsafe在修改对象属性值的性能

 

import sun.misc.Unsafe;

import java.lang.reflect.Field;

/**
 * Created by yuanchen.xuyc on 2016/7/8.
 * 测试反射和unsafe
 */
public class TestUnsafe {

    static Field fieldA;
    static Field fieldB;
    static long aFieldOffset;
    static long bFieldOffset;
    static Unsafe unsafe;

    static long loopSize = 50000000;
    static long preHot = 3000;
    static {

        try {
            //1.反射
            fieldA = Bean.class.getDeclaredField("a");
            fieldB = Bean.class.getDeclaredField("b");

            fieldA.setAccessible(true);
            fieldB.setAccessible(true);

            //获取unsafe实例
            Field field = Unsafe.class.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            unsafe = (Unsafe)field.get(null);

            //获取属性a,b的偏移量
            aFieldOffset = unsafe.objectFieldOffset(fieldA);
            bFieldOffset = unsafe.objectFieldOffset(fieldB);

        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

    }
    public static void main(String args[]) throws IllegalAccessException {

        testReflection();
        testUnsafe();
        testDirect();
    }

    static class Bean{
        private int a;
        private int b;

    }


    private static void testReflection() throws IllegalAccessException {
        Bean bean = new Bean();

        //预热
        for(int i=0; i<preHot;i++){
            fieldA.set(bean,i);
            fieldB.set(bean,i);
        }

        long start = System.currentTimeMillis();
        for(int i=0; i<loopSize;i++){
            fieldA.set(bean,i);
            fieldB.set(bean,i);
        }
        long end = System.currentTimeMillis();
        System.out.println("反射值set耗费:"+(end-start)+"ms");
    }

    private static void testUnsafe(){

        Bean bean = new Bean();

        //预热
        for(int i=0; i<preHot;i++){
            unsafe.putInt(bean, aFieldOffset, i);
            unsafe.putInt(bean, bFieldOffset, i);
        }

        long start = System.currentTimeMillis();
        for(int i=0; i<loopSize;i++){
            unsafe.putInt(bean, aFieldOffset, i);
            unsafe.putInt(bean, bFieldOffset, i);
        }
        long end = System.currentTimeMillis();
        System.out.println("unsafe set耗费:"+(end-start)+"ms");

    }

    private static void testDirect(){
        Bean bean = new Bean();

        for(int i=0; i<preHot;i++){
            bean.a = i;
            bean.b = i;
        }

        long start = System.currentTimeMillis();
        for(int i=0; i<loopSize;i++){
            bean.a = i;
            bean.b = i;
        }
        long end = System.currentTimeMillis();
        System.out.println("direct set耗费:"+(end-start)+"ms");
    }
}


 在x240机器上测试的结果如下:

 

反射值set耗费:2697ms

unsafe set耗费:164ms

direct set耗费:100ms

从数据上可以看出,unsafe的耗时接近于直接赋值,而反射的性能和unsafe比起来不在一个数量级上。

(2)提供了compareAndSwap,用于实现无锁计数功能

 直接上代码,比较几种计数的效率

import sun.misc.Unsafe;

import java.lang.reflect.Field;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * Created by yuanchen.xuyc on 2016/7/8.
 */
public class TestCouter {
    private static int threadNum = 500;
    private static int num_to_increment = 1000000;

    public static void main(String args[]) throws InterruptedException {

        ExecutorService executorService = Executors.newFixedThreadPool(threadNum);
        MyCounter synCounter = new SyncCounter();
        MyCounter lockCounter = new LockCounter();
        MyCounter atomicCounter = new AtomicCounter();
        MyCounter unsafeCounter = new UnsafeCounter();

        long startTime = System.currentTimeMillis();
        for(int i=0;i<threadNum;i++){
            executorService.submit(new Count(synCounter,num_to_increment));
        }
        executorService.shutdown();
        executorService.awaitTermination(1, TimeUnit.MINUTES);
        long endTime = System.currentTimeMillis();
        System.out.println("计数到:"+synCounter.getCounter());
        System.out.println("计数耗时:"+(endTime-startTime) + "ms");
    }
}
class Count implements Runnable{
    private MyCounter myCounter;
    private int count;
    Count(MyCounter myCounter,int count){
        this.myCounter = myCounter;
        this.count = count;
    }
    public long getCounter(){
        return myCounter.getCounter();
    }
    public void run() {
        for(int i=0;i<count;i++) {
            myCounter.increment();
        }
    }
}

/**
 * 利用synchronized加锁实现同步
 */
class SyncCounter extends MyCounter{

    private long counter = 0l;

    public synchronized void increment(){
        counter++;
    }

    public long getCounter(){
        return counter;
    }
}

/**
 * 使用读写锁实现
 */
class LockCounter extends MyCounter{
    private ReentrantReadWriteLock.WriteLock lock = new ReentrantReadWriteLock().writeLock();
    private long counter = 0l;

    public void increment(){
        try{
            lock.lock();
            counter++;
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }

    }

    public long getCounter(){
        return counter;
    }
}

/**
 * 使用原子类
 */
class AtomicCounter extends MyCounter{
    private AtomicLong atomicLong = new AtomicLong(0);
    public void increment(){
        atomicLong.incrementAndGet();
    }
    public long getCounter(){
        return atomicLong.get();
    }
}

/**
 * 使用unsafe
 */
class UnsafeCounter extends MyCounter{
    private volatile long counter = 0l;
    private Unsafe unsafe;
    private long offset;
    private Unsafe getUnsafe(){
        try {
            Field field = Unsafe.class.getDeclaredField("theUnsafe");
            field.setAccessible(true);

            unsafe = (Unsafe)field.get(null);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return unsafe;
    }

    UnsafeCounter(){
        unsafe = getUnsafe();
        try {
            offset = unsafe.objectFieldOffset(UnsafeCounter.class.getDeclaredField("counter"));
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }

    public void increment(){
        long before = counter;
        while(!unsafe.compareAndSwapLong(this,offset,before,before+1)){
            before = counter;
        }
    }

    public long getCounter(){
        return counter;
    }
}

abstract class MyCounter{
    abstract void increment();
    abstract long getCounter();
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值