创建型模式之单例模式

▼魔方 西西 提交于 2020-03-14 01:49:26

一、介绍

类的单例设计模式,就是采用一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(静态方法)。

二、单例模式八种方式

  1. 饿汉式(静态常量)
  2. 饿汉式(静态代码块)
  3. 懒汉式(线程不安全)
  4. 懒汉式(线程安全,同步方法)
  5. 懒汉式(线程安全,同步代码块)
  6. 双重检查
  7. 静态内部类
  8. 枚举

1、饿汉式(静态常量)

class Singleton{

//    1.构造器私有化,外部不能new
    private Singleton(){}

//    2.本类内部创建对象实例
    private final static Singleton instance = new Singleton();

//    3.提供一个共有的静态方法,返回实例对象
    public static Singleton getInstance(){
        return instance;
    }
}

优缺点:

  1. 优点:方法简单,在类装载的时候就完成实例化,避免线程同步。
  2. 缺点:没有达到 Lazy Loading 的效果。如果从始至终没有使用这个实例,则造成内存浪费。
  3. 结论:可用,但可能造成内存浪费。

2、饿汉式(静态代码块)

class Singleton{
    
//    1.构造器私有化,外部不能new
    private Singleton(){}
    
//    2.本类内部创建对象实例
    private static Singleton instance;
    
    static { // 静态代码块中创建单例对象
        instance = new Singleton();
    }
    
//    3.提供一个公有的静态方法,返回实例对象
    public static Singleton getInstance(){
        return instance;
    }
}

优缺点:

  1. 方法和第一种类似,只不过将类实例化的过程放在了静态代码块中。优缺点和第一种方式一样。
  2. 结论:可用,但可能造成内存浪费。

3、懒汉式(线程不安全)

class Singleton{
    private static Singleton instance;

    private Singleton(){}

//    提供一个静态的公有方法,当使用到该方法时,才去创建 instance
//    即懒汉式
    public static Singleton getInstance(){
        if(instance == null){
            instance = new Singleton();
        }
        return instance;
    }
}

优缺点:

  1. 优点:起到了 Lazy Loading 的效果,但只能在单线程下使用。
  2. 缺点:如果在多线程下,一个线程进入了 if(instance == null) 判断语句中,没执行完,另一个线程也通过了这个判断语句,这时就产生了多个实例。
  3. 结论:在实际开发中,不要使用这种方式。

4、懒汉式(线程安全,同步方法)

class Singleton{
    private static Singleton instance;

    private Singleton(){}

//    提供一个静态的公有方法,加入同步处理的代码,解决线程安全问题
    public static synchronized Singleton getInstance(){
        if(instance == null){
            instance = new Singleton();
        }
        return instance;
    }
}

优缺点:

  1. 优点:解决了线程安全问题。
  2. 缺点:效率太低了。每个线程想获得类的实例都要执行 getInstance() 方法同步,只有第一次实例化才要用到同步锁,后面的要获得该类的实例,直接 return 就行了。
  3. 结论:在实际开发中,不推荐使用这种方式。

5、懒汉式(线程安全,同步代码块)

class Singleton{
    private static Singleton instance;

    private Singleton(){}

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

优缺点:

  1. 本质跟上一种方法一样。
  2. 结论:不推荐使用。

6、双重检查

class Singleton{
    private static volatile Singleton instance;

    private Singleton(){}

//    提供一个静态的公有方法,加入双重检查代码,解决线程安全问题,同时解决懒加载问题
//    同时保证了效率,推荐使用
    public static Singleton getInstance(){
        if(instance == null){
            synchronized(Singleton.class){
                if(instance == null){
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

优缺点:

  1. 优点:Double-Check 概念是多线程开发中常使用的。延迟加载,效率较高。
  2. 结论:在实际开发中,推荐使用这种单例设计模式。

7、静态内部类

class Singleton{

    private Singleton(){}

//    写一个静态内部类,该类中有一个静态属性
    private static class SingletonInstance{
        private static final Singleton INSTANCE = new Singleton();
    }

//    提供一个静态的公有方法,直接返回 SingletonInstance.INSTANCE
    public static synchronized Singleton getInstance(){
        return SingletonInstance.INSTANCE;
    }
}

优缺点:

  1. 原理:这种方式采用了类装载的机制来保证初始化实例时只有一个线程。静态内部类方式在 Singleton类被装载时并不会立即实例化,而是在需要实例化时,调用getInstance 方法,才会装载 SingletonInstance 类,从而完成 Singleton 的实例化。类的静态属性只会在第一次加载类的时候实例化,所以在这里,JVM帮助我们保证了线程的安全,在类进行初始化时,别的线程是无法进入的。
  2. 优点:避免了线程不安全,利用静态内部类特点实现延迟加载,效率高。
  3. 结论:推荐使用。

8、枚举

//使用枚举,可以实现单例,推荐
enum Singleton{
    INSTANCE; //属性

//    public void sayOK(){
//        System.out.println("ok~");
//    }
}

优缺点:

  1. 借助JDK1.5 中添加的枚举来实现单例模式。不仅能避免多线程同步问题,而且还能防止反序列化重新创建对象。
  2. 结论:推荐使用。

三、单例模式在 JDK 应用的源码分析

  1. JDK中,java.lang.Runtime 就是经典的单例模式(饿汉式)
  2. 代码:

四、单例模式注意事项和细节说明

  1. 单例模式保证了系统内存中该类只存在一个对象,节省了系统资源,对于一些需要创建销毁的对象,使用单例模式可以提高系统性能。
  2. 当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用new。
  3. 单例模式使用的场景:需要频繁的进行创建和销毁的对象、创建对象时耗时过多或耗费资源过多(即:重量级对象),但又经常用到的对象、工具类对象、频繁访问数据库或文件的对象(比如数据源、session工厂等)。
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!