A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 徐启坤 中级黑马   /  2013-6-23 15:18  /  1517 人查看  /  1 人回复  /   2 人收藏 转载请遵从CC协议 禁止商业使用本文

对于单例模式的“懒汉式”和“饿汉式”大家都比较熟悉了,先简单说一下
饿汉式就比较简单了,今天的重点是后面的懒汉式:

class Single
{
    private static final Single s = new Single();
    private Single(){}
    public static Single getInstance()
    {
        return s;
    }
}
懒汉式就是延迟加载,但一般的形式会出现线程安全问题,所以出现了以下的改良版:通过加锁保证线程安全,双重判断提高效率
class Single
{
    private static Single s = null;
    private Single(){}
    public static  Single getInstance()
    {
        if(s==null)
        {
            synchronized(Single.class)
            {
                if(s==null)
                    s = new Single();
            }
        }
        return s;
    }
}

以上就是我们通常见到的单例模式,但是昨天看到了一篇博客,今天将主要内容跟大家分享一下:
     java平台内存模型中有一个叫“无序写”(out-of-order writes)的机制。正是这个机制导致了双重检查加锁方法的失效。这个问题的关键在上面代码中的:s = new Singleton(); 这行其实做了两个事情:1、调用构造方法,创建了一个实例。2、把这个实例赋值给 s 这个实例变量。可问题就是,这两步jvm是不保证顺序的。也就是说。可能在调用构造方法之前,s 已经被设置为非空了。
     也就是说如果有一个A线程在调用构造方法之前将 s 变量置为非空了,正好在此时停了下来,线程B过来判断 s 为非空,则将 s 返回了,但返回的却是无效的引用,所以导致错误。
     简便的解决方法就是使用内部类的形式:

public class Singleton {  
   //内部类
    private static class SingletonHolder{  
        //单例变量   
        private static Singleton instance = new Singleton();  
    }  
      
    //私有化的构造方法,保证外部的类不能通过构造器来实例化。  
    private Singleton() {}  
      
    //获取单例对象实例  
    public static Singleton getInstance() {  
        System.out.println("我是内部类单例!");  
        return SingletonHolder.instance;  
    }  
}

因为内部类也是只有在调用时才会加载,所以同样实现了延迟加载的功效。
下面是原博客的地址:http://blog.csdn.net/xu_song/article/details/9142383

另外还有一点是我自己总结的一点关于单例的小知识:
大家都知道单例有“懒汉式”和“饿汉式”其实张老师提到过单例还有一种实现方式,那就是“枚举”。
因为枚举也是不能用 new 来创建对象的,所以当枚举中只定义了一个对象值时,就成了特殊的单例。

这里我想说的一点就是,以前看过论坛里有说过,通过暴力反射可以获得单例的构造函数,然后再通过构造函数就可以创建对象,这样就破坏了单例的对象唯一性。
但是如果使用枚举来实现单例的话,我自己实验了一下,通过暴力反射并没有成功的创建对象,也许是我做的有疏漏,有兴趣的同学可以自己来试验一下,并分享一下结果。


1 个回复

倒序浏览
谢谢分享了!
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马