单例模式---懒汉与饿汉模式和静态内部类实现

单例模式是最基本的 java 设计模式之一
主要有两种设计方法,主要分为饿汉式与懒汉式

饿汉式比较简单,直接使用常量即可,主要代码如下:

    private static final SingleModel INSTANCE = new SingleModel();

    private SingleModel()
    {

    }

    public static SingleModel getInstance()
    {
        return INSTANCE;
    }

这种设计方法是比较推荐的,虽然直接加载静态类会多消耗内存,但在运行期较简单,且代码设计易懂,不容易出问题

懒汉式懒汉式采用懒加载的形式,只有在使用的时候才会加载类,较简单的设计方式如下

private static SingleModel INSTANCE = null;

    private SingleModel()
    {

    }

    public static SingleModel getInstance()
    {
        if (null == INSTANCE)
        {
            INSTANCE = new SingleModel();
        }
        return INSTANCE;
    }

但这种设计在多线程环境下会出问题,当两个线程同时监测到 INSTANCE 为空时,会生成两份 SingleModel 的内存,解决这个问题可以使用在方法上加synchronized的方式来解决,但这种方式每次getInstance时都使用阻塞方法效率较低,可以使用双 null 检查的方法改进

public static SingleModel getInstance()
    {
        if (null == INSTANCE)
        {
            synchronized (INSTANCE)
            {
                if (null == INSTANCE)
                {
                    INSTANCE = new SingleModel();
                }
            }
        }
        return INSTANCE;
    }

此时可以避免两个线程加载多次的SingleModel的问题,且只要INSTANCE被初始化,就不存在阻塞问题,使用这个方法还需要注意指令重新排序的问题,需要在变量INSTANCE上添加关键字volatile,具体原因可以参照博文http://blog.csdn.net/jm_heiyeqishi/article/details/51052889
但这个方式还是需要每次都判断INSTANCE是否为空,增加了运算效率,可以使用静态内部类的方式改进
注意双重检查的方法在 JDK1.5之前由于 java 内存机制的问题并不能保证正确运行,但 JDK1.5之后该问题已经不存在

静态内部类方式实现单例模式
代码如下

private SingleModel()
    {

    }

    static class SingleInstanceModel
    {
        private static final SingleModel INSTANCE = new SingleModel();
    }

    public static SingleModel getInstance()
    {
        return SingleInstanceModel.INSTANCE;
    }

此方法使用了懒加载,且不再需要每次都判断 INSTANCE 是否为空,且在多线程环境下可以完美运行
综上所述,单例模式优先选用饿汉模式,如果需要使用懒加载最好使用静态内部类的方式实现,或者使用双 null 检查的方式

为何静态内部类可以实现懒加载?
其实静态内部类与普通类除了在访问时需要使用外部类做入口外,其他地方与普通类没有任何区别,因此只要不加载静态内部类,其静态常量就不会被初始化

单例模式的反序列化
为保证单例,单例模式类反序列化时需要实现 readResolve 方法,具体实现比较简单,如下即可:

    private Object readResolve()
    {
        return getInstance();
    }

线程单例模式
此处实现一个每个线程独享的单例类

private static final Map<Thread, TestSingle> instance = new HashMap<>();

    public static TestSingle getInstance()
    {
        Thread thread = Thread.currentThread();
        if (null == instance.get(thread))
        {
            synchronized (TestSingle.class)
            {
                if (null == instance.get(thread))
                {
                    TestSingle test = new TestSingle();
                    instance.put(thread, test);
                }
            }
        }
        return instance.get(thread);
    }

反射方法可能会破坏单例模式
所有的私有构造器都有可能被使用反射构造多个实例,此时可以在构造方法中增加校验,抛出异常来避免这种情况.代码如下:

private static boolean flag = false;

private SingleModel()
    {
        synchronized (SingleModel.class)
        {
            if (!flag)
            {
                flag = true;
            }
            else
            {
                throw new RuntimeException("单例模式正在被攻击");
            }
        }
    }

参考资料:
http://blog.csdn.net/hudashi/article/details/6949379
http://blog.csdn.net/chenchaofuck1/article/details/51702129

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值