单例模式

又回到了这里,单例模式。
毕业前一直如雷贯耳,也曾亲密接触,但是只得其表不得其里。
终于毕业后在工作中用到了单例。那种感觉就是:“复行数十步,豁然开朗。土地平旷,屋舍俨然,有良田美池桑竹之属。”


正文:
先说一下学习链接,菜鸟教程
什么是单例?
就是有这么一个类,它只能创建自己的对象,有且只有一个自己的对象。并向外部提供一个访问这个对象的方式。
单例解决了什么麻烦?(为什么使用单例)
1、如果一个类频繁的创建然后销毁,麻烦不麻烦?很麻烦,用单例,只创建一次,每次需要的时候直接调用就行。
2、控制实例数目,节省系统资源。
单例的特点是什么?(使用单例需要注意的地方)
1、构造函数必须私有化。
2、不能继承。
3、需要同步锁synchronized。
解释:
1、为什么构造函数必须私有化?如果不私有化,那默认是public,那就可以无限使用Singleton singleton = new Singleton();那就会不断实例化单例类。违反了单例的设计模式,提供不了单例所带来的优势。
2、为什么不能继承?因为构造函数私有化了。如果继承,子类的构造函数默认super();父类私有化的构造函数怎么super();?
3、为什么需要同步锁synchronized?防止多线程进入造成单例类被多次实例化。

单例代码怎么实现?
(线程安全是指:避免多个线程同时使用一个对象;不安全反之。)
(Lazy初始化是指:使用到的时候才进行初始化;反之就是在类加载的时候就初始化:即时加载)

1、懒汉式,线程不安全,Lazy初始化
不支持多线程,因为没有加锁,synchronized。

package test;

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

2、懒汉式,线程安全,Lazy初始化
支持多线程,加锁synchronized,但是效率低。

package test;

public class Singleton {
    
    private Singleton(){}
    
    private static Singleton instance;
        
    public synchronized static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

3、饿汉式,线程安全,即时加载
没有加锁,执行效率高,类加载就初始化,浪费内存,容易产生垃圾。
即时加载容易理解,类初始化的时候,就要new Singleton();那既然没有synchronized,为什么还会线程安全?
因为classloader机制避免了多线程的同步问题。那什么是classloader机制?为什么避免了多线程?查了一些类加载的资料,还不是很懂,目前的理解是:多个线程同时实例化对象的时候,如果不存在Singleton对象,则触发类的初始化,如果存在Singleton对象,则直接调用。(类加载的文章链接点击这里

package test;

public class Singleton {
    
    private Singleton(){}
    
    private static Singleton instance = new Singleton();
        
    public synchronized static Singleton getInstance() {
        return instance;
    }
}

4、双检锁/双重校验锁(DCL,即 double-checked locking)
线程安全,懒加载,并且高性能。
其实这种单例我是没懂,单写出来记录一下。疑问点有两点。一是volatile关键字,看了例子,大概是不具备原子性,具备可见性。比synchronized更轻量级的同步机制,单原理和使用场景还没搞明白。二是双城instance == null 的目的。

package test;

public class Singleton {
    
    private Singleton(){}
    
    private volatile static Singleton instance;
        
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

5、登记式/静态内部类//(此单例模式代码摘抄自菜鸟教程,因为这个好多地方我还没看懂)
这种单例模式是在饿汉式单例的升级版,为什么?因为在饿汉式的基础上达到lazy loading的效果。还能达到双检锁单例的功效。

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

6、枚举式
线程安全,非懒加载。(介绍枚举式单例详细的一篇博客
实现单例最佳的方式,自动支持序列化机制,防止被多次实例化。
线程安全的原因:枚举类在被虚拟机加载的时候会保证线程安全的被初始化。
在序列化方面:枚举的序列化和反序列化是有特殊定制的。这就可以避免反序列化过程中由于反射而导致的单例被破坏问题。
线程安全自然不用多说,那么序列化和反序列化是什么呢?先把问题放在这儿。

package test;

public enum Singleton {
    instance;//public static final Singleton  instance;
}
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容