单例模式

什么是单例模式:

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

注意:

1、单例类只能有一个实例。

2、单例类必须自己创建自己的唯一实例。

3、单例类必须给所有其他对象提供这一实例。


1.不好的方法:懒汉式(单线程下)

评价:lazy loading,线程不安全,在多线程下不能工作


//懒汉式
public class Singleton {
    private static Singleton instance;
    public Singleton(){
    }
    public static Singleton getInstance(){
        if(instance==null){
            instance=new Singleton();
        }
        return instance;
    }
}

注意:  为什么会出现线程问题,线程A希望使用Singleton,调用getInstance()方法。因为是第一次调用,A就发现instance是null的,于是它开始创建实例,就在这个时候,CPU发生时间片切换,线程B开始执行,它要使用Singleton,调用getInstance()方法,同样检测到instance是null——注意,这是在A检测完之后切换的,也就是说A并没有来得及创建对象——因此B开始创建。B创建完成后,切换到A继续执行,因为它已经检测完了,所以A不会再检测一遍,它会直接创建对象。这样,线程A和B各自拥有一个Singleton的对象——单例失败!

所以我们需要下面的方法来改进。


2.不好的方法:懒汉式(多线程)

评价:lazy loading,加了Synchronize,线程安全,但是同时也会因为加了synchronize而影响效率

//懒汉  多线程
public class Singleton1 {
    private static Singleton1 instance ;
    public Singleton1(){}
    public static synchronized Singleton1 getInstance1(){
        if(instance==null){
            instance =new Singleton1();
        }
        return instance;
    }
}

  

3.可行的方法:饿汉模式

线程安全,非懒加载

//饿汉模式
public class Singleton2 {
    private static Singleton2 instance = new Singleton2();
    public Singlenton2(){}
    public static Singleton2 getInstance2() {
        return instance;
    }
}

4.双检锁

原理可参照下面两个链接的内容:

https://blog.csdn.net/u011889786/article/details/53364618

https://www.jianshu.com/p/d82cbb83f393?from=timeline

第二个图片中线程执行顺序的图片有问题,参照第一个链接。

//双检锁
public class Singleton4 {
    private volatile static  Singleton4 singleton4 ;
    public Singleton4(){}
    public static Singleton4 getInstance4(){
        if(singleton4==null){
            synchronized(Singleton4.class){
                if(singleton4==null){
                    singleton4=new Singleton4();
                }
            }
        }
        return singleton4;
    }
}

5.推荐方法:静态内部类

这种方法即实现了懒加载,又保证了实例化instance时只有一个线程。饿汉式中只要类被装载,那么instance就会被实例化,而在此方法中Singleton类被装载了,instance也不一定被实例化。因为SingletonHolder类没有被主动使用。只有通过显式调用 getInstance 方法时,才会显式装载 SingletonHolder 类,从而实例化 instance。

想象一下,如果实例化 instance 很消耗资源,所以想让它延迟加载,另外一方面,又不希望在 Singleton 类加载时就实例化,因为不能确保 Singleton 类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化 instance 显然是不合适的。这个时候,这种方式相比第3种种方式就显得很合理。

public class Singleton3 {
    private static class SingletonHolder{
        private static final Singleton3 instance = new Singleton3();
    }
    private Singleton3 (){}
    public static final Singleton3 getInstance(){
        return SingletonHolder.instance;
    }
}

6.枚举

描述:这种实现方式还没有被广泛采用,但这是实现单例模式的最佳方法。它更简洁,自动支持序列化机制,绝对防止多次实例化

这种方式是 Effective Java 作者 Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。不过,由于 JDK1.5 之后才加入 enum 特性,用这种方式写不免让人感觉生疏,在实际工作中,也很少用。

经验之谈:一般情况下,不建议使用第 1 种和第 2 种懒汉方式,建议使用第 3 种饿汉方式。只有在要明确实现 lazy loading 效果时,才会使用第 5 种登记方式。如果涉及到反序列化创建对象时,可以尝试使用第 6 种枚举方式。如果有其他特殊的需求,可以考虑使用第 4 种双检锁方式。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值