Unsafe类

今天读到AtomicInteger类源码时,发现它内部持有一个Unsafe类的实例,于是想点开去看源码,结果没有点开,上网一查,才知道原来这个类是JAVA中的一个后门,用来对内存进行操作,我想正是因为这类操作存在较大风险,所以这个类才会被命名为Unsafe(不安全的意思)吧。

Unsafe的源码:http://www.docjar.com/html/api/sun/misc/Unsafe.java.html

也正是因为这个类的使用存在风险,所以这个类被加了很多限制,主要如下:

1、 首先是final的,不能被继承,

2、 构造器是私有的,也就是不能通过被外部调用创建实例,

3、 它提供了一个静态方法getUnsafe()类可以获取它的一个实例,AtomicInteger中就是用这个方法获取实例的,但如果你在代码中使用Unsafe.getUnsafe来获取它的实例,它却会抛出一个SecurityException("Unsafe"),这是因为在这个方法先获取了调用这个方法的Class实例,并获取其ClassLoader,判断其是否为空,为空,则意思着,这是一个被BoostrapClassLoader类加载器加载的JAVA核心类,此时会正常返回其实例,否则抛异常,所以我们在自己的代码里无法获取其实例。


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

说了这么多,那这个类岂不是没法用了?其实还是有办法可以获取其实例的,就是通过反射,代码如下:

<span style="white-space:pre">	</span>try
        {
            Field field = Unsafe.class.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            Unsafe unsafe = (Unsafe) field.get(null);
            return unsafe;
        }
        catch(SecurityException e1)
        {
            e1.printStackTrace();
            return null;
        }
        catch(NoSuchFieldException e2)
        {
            e2.printStackTrace();
            return null;
        }
        catch(IllegalArgumentException e3)
        {
            e3.printStackTrace();
            return null;
        }
        catch(IllegalAccessException e4)
        {
            e4.printStackTrace();
            return null;
        }

这样就可以获取一个Unsafe类的实例的。

Unsafe的作用

主要用于并发控制,例如在AtomicInteger类中正是利用Unsafe的putOrderedInt 和compareAndSwapInt方法来实现并发控制。使用这个方法前要先使用objectFieldOffset方法来获取类中的变量相对对象首地址的偏移量。
方法原型如下:
public final native boolean compareAndSwapInt(Object o, long ffset,
 int expected, int x);
 

Unsafe中有个方法为allocateInstance(Class c),能够在不调用类的构造器的情况下创建类的实例,


import java.lang.reflect.Field;

import sun.misc.Unsafe;

/**
 * 饿汉式单例模式
 * 
 * @ClassName: Singleton
 * @Description: TODO(这里用一句话描述这个类的作用)
 * @author zzh
 * @date 2015年9月25日 下午9:59:24
 * @version 1.0
 */
class Singleton
{
    public int id;

    public static Singleton singleton = new Singleton();

    private Singleton()
    {
        id = 1;
    }

    public static Singleton getInstance()
    {
        return singleton;
    }

    public int getId()
    {
        return id;
    }
}

/**
 * 对比演示分别使用Unsafe类和反射创建饿汉式单例类实例的结果
 * 
 * @ClassName: CreateInstanceWithUnsafe
 * @Description: TODO(这里用一句话描述这个类的作用)
 * @author zzh
 * @date 2015年9月25日 下午10:09:01
 * @version 1.0
 */
public class CreateInstanceWithUnsafe
{
    /**
     * 通过反射获取Unsafe中的theUnsafe实例
     * 
     * @Title: getUnsafe
     * @Description: TODO(这里用一句话描述这个方法的作用)
     * @param @return 设定文件
     * @return Unsafe 返回类型
     * @throws
     */
    public static Unsafe getUnsafe()
    {
        try
        {
            Field field = Unsafe.class.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            Unsafe unsafe = (Unsafe) field.get(null);
            return unsafe;
        }
        catch(SecurityException e1)
        {
            e1.printStackTrace();
            return null;
        }
        catch(NoSuchFieldException e2)
        {
            e2.printStackTrace();
            return null;
        }
        catch(IllegalArgumentException e3)
        {
            e3.printStackTrace();
            return null;
        }
        catch(IllegalAccessException e4)
        {
            e4.printStackTrace();
            return null;
        }
    }

    @SuppressWarnings({"rawtypes","unused"})
    public static void main(String[] args) throws InstantiationException
    {
        Unsafe unsafe = getUnsafe();
        if (unsafe == null)
        {
            System.out.println("Get Null.");
            System.exit(0);
        }

        Singleton singleton = (Singleton) unsafe
                .allocateInstance(Singleton.class);
        System.out.println("使用Unsafe获取的Singleton实例: " + singleton);
        System.out.println("使用Unsafe获取的Singleton实例与单例类中实例是否相等: "
                + (singleton == Singleton.getInstance()));
        System.out.println("使用Unsafe获取的Singleton实例中id值:" + singleton.getId());
        System.out.println("单例类中中id值:" + Singleton.getInstance().getId());

        // 尝试使用反射创建Singleton类的实例
        Class c = Singleton.class;
        try
        {
            Singleton singleton2 = (Singleton) c.newInstance();
        }
        catch(IllegalAccessException e)
        {
            e.printStackTrace(System.out);
        }
    }
}

运行结果:


使用Unsafe获取的Singleton实例: other.Singleton@7f4ec
使用Unsafe获取的Singleton实例与单例类中实例是否相等: false
使用Unsafe获取的Singleton实例中id值:0
单例类中中id值:1
java.lang.IllegalAccessException: Class other.CreateInstanceWithUnsafe can not access a member of class other.Singleton with modifiers "private"
	at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:95)
	at java.lang.Class.newInstance0(Class.java:368)
	at java.lang.Class.newInstance(Class.java:327)
	at other.CreateInstanceWithUnsafe.main(CreateInstanceWithUnsafe.java:102)

结果表明:使用Unsafe类的allocateInstance方法对饿汉式单例类新建对象成功了,并且可以看出Singleton类的id被初始化为0,并没有调用构造器,因为如果调用了构造器,id就应该被初始化为1;而使用反射对饿汉式单例类新建对象报了错,因为Singleton类的构造器为private,所以无法被调用。

对饿汉式单例如此,那懒汉式是不是也一样呢?这个应该是一样的,毕竟二者都是通过把构造器私有化来保证单例的,只是初始化的时间不同而已。那么是不是就没有办法保证单例了呢?

记得在网上看单例时,看到有人说是《Effectivejava》(这本书很经典,可惜我还没看过,我只看了《Thinkingin java》,看了大半,还没看完)中有提到用枚举类型来实现单例,那么用枚举实现的单例能用这两种方法创建实例吗?今天我还特意学习了下枚举类型,在这里就不讲枚举了,有时间再写篇文章讲讲枚举类型,现在看对于枚举类型,用Unsafe能否新建对象呢?

代码:


import sun.misc.Unsafe;

/**
 * 对比演示分别使用Unsafe类和反射创建枚举实现的单例类实例的结果
 * 
 * @ClassName: TestEnum
 * @Description: TODO(这里用一句话描述这个类的作用)
 * @author zzh
 * @date 2015年9月25日 下午10:57:28
 * @version 1.0
 */
public class TestEnum
{

    @SuppressWarnings({"rawtypes","unused"})
    public static void main(String[] args)
    {
        Unsafe unsafe = CreateInstanceWithUnsafe.getUnsafe();
        if (unsafe == null)
        {
            System.out.println("Get Null.");
            System.exit(0);
        }

        try
        {
            EnumSingleton enumSingleton = (EnumSingleton) unsafe
                    .allocateInstance(EnumSingleton.class);
            System.out.println(enumSingleton);
            if (enumSingleton == null)
            {
                System.out.println("null");
            }
            if (enumSingleton instanceof other.EnumSingleton)
            {
                System.out.println("true");
            }
        }
        catch(InstantiationException e)
        {
            e.printStackTrace(System.out);
        }
        
        Class cl = EnumSingleton.class;
        try
        {
            EnumSingleton e = (EnumSingleton) cl.newInstance();
        }
        catch(InstantiationException | IllegalAccessException e)
        {
            e.printStackTrace(System.out);
        }
    }
}

/**
 * 枚举实现的单例
 * 
 * @ClassName: EnumSingleton
 * @Description: TODO(这里用一句话描述这个类的作用)
 * @author zzh
 * @date 2015年9月25日 下午10:56:35
 * @version 1.0
 */
enum EnumSingleton
{
    SINGLETON;
}

运行结果:

null
true
java.lang.InstantiationException: other.EnumSingleton
	at java.lang.Class.newInstance0(Class.java:359)
	at java.lang.Class.newInstance(Class.java:327)
	at other.TestEnum.main(TestEnum.java:47)

结果仍然是使用Unsafe可以创建实例,使用反射抛出异常。

但结果中却有些特别的东西需要注意:用Unsafe的allocateInstance方法创建的实例输出为null,而用instanceof other.EnumSingleton时却是true,这是怎么回事?

对于这个我想到几个月前我研究null时想知道null在内存中存的是什么值,我当时猜测是0,因为读Thinking injava时,看到类的初始化过程时,当时说是先分配空间,然后把这些空间全部置0,以保证所有变量都被初始化,所以即使没有在构造器中初始化,int型仍会被初始化为0,对象会被初始化为null。所以我猜想是0,但是怎么验证呢?当时我已经学会了使用JNI接口,所以用JNI接口,写了个方法,在JAVA里传入null,然后在C语言代码里读取null内存中的值输出,结果为0。关于JNI这部分,以后有机会我会写篇文章来介绍。




编程技术交流请加QQ群:点击链接加入群【Just Do IT】:https://jq.qq.com/?_wv=1027&k=478lBF3


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值