单例模式详解

单例模式(创建型模式)

基本介绍

单例模式指在整个应用中,某个类只存在一个实例对象,且该类只提供一个取得其对象实例的方法。

应用

JDK的Runtime中使用到了饿汉式单例模式

单例模式的八种方式

(1)饿汉式(静态常量)

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

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

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

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

(6)双重检查

(7)静态内部类

(8)枚举

饿汉式(静态常量)(可以使用)

代码实现

//饿汉式(静态常量)
public class Hunger {
     private Hunger(){

     }
     private static final Hunger instance=new Hunger();
     public static Hunger getInstance(){
         return instance;
     }

}

优缺点

  1. 优点:写法简单,类加载时完成了实例化,避免了线程同步问题。

  2. 缺点:类加载时完成了实例化,没达到lazy loading(惰性加载)的效果,若从不会用到这个实例会造成内存浪费。

    原因:不一定是通过调用getInstance()方法导致类加载,有可能是其他的方式导致类加载

  3. 总结:这种单例模式可用,可能造成内存浪费。

饿汉式(静态代码块)(可以使用)

代码实现

//饿汉式(静态代码块)
public class Hunger {
     private Hunger(){

     }
     private static Hunger instance;
     static {
          instance=new Hunger();
     }
     public static Hunger getInstance(){
         return instance;
     }

}

优缺点

同上述静态属性的优缺点

懒汉式(线程不安全)

代码实现

public class Lazy {
    private Lazy(){

    }
    private static Lazy instance;

    public static Lazy getInstance() {
        if(instance==null){
            instance=new Lazy();
        }
        return instance;
    }
}

优缺点

  1. 优点:起到了lazy loading的效果,但只能在单线程下使用,
  2. 缺点:在多线程下可能产生多个实例,多线程不安全
  3. 结论:实际开发中不建议使用。

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

代码实现

//懒汉式(线程安全)
public class Lazy {
    private Lazy(){

    }
    private static Lazy instance;
    //加入同步处理代码,解决线程不安全问题
    public static synchronized Lazy getInstance() {
        if(instance==null){
            instance=new Lazy();
        }
        return instance;
    }
}

优缺点

  1. 优点:解决了线程不安全问题
  2. 缺点:效率低,每个线程想要获得类的实例时,执行getInstance()方法都要进行同步,
  3. 实际开发时不推荐使用

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

代码实现

//懒汉式(线程安全)
public class Lazy {
    private Lazy(){

    }
    private static Lazy instance;
    public static Lazy getInstance() {
        if(instance==null){
            //此处加入了同步代码块,当两线程都进入了判断语句,还是有可能造成两个实例的产生。
           synchronized (Lazy.class){
               instance=new Lazy();
           }
        }
        return instance;
    }
}

优缺点

  1. 这种方法本意是对懒汉式同步方法的改进,因为懒汉式同步方法的效率太低。将其更改为同步产生实例的代码块
  2. 但这种同步方法并不能起到线程同步的作用,只要两个线程都进入了判断语句,还是有可能产生多个实例。
  3. 实际开发中不使用。

双重检查(推荐使用)

代码实现

//双重检测(线程安全)
public class Lazy {
    private Lazy(){

    }
    //volatile:保证了一致性,不保证原子性,禁止指令重拍
    private volatile static Lazy instance;
    public static Lazy getInstance() {
        if(instance==null){
            //加入同步代码块
           synchronized (Lazy.class){
               //进行第二次检测
               if(instance==null){
                   instance=new Lazy();
               }
           }
        }
        return instance;
    }
}

优缺点

  1. Double-Check概念是多线程开发中经常使用的,进行了两次if(instance==null)检查,保证了线程安全。
  2. 这样的设计使得实例化代码只执行一次:后面的·1访问都是直接return实例化对象,避免反复进行方法同步。
  3. 优点:线程安全,延迟加载,效率较高,
  4. 结论:实际开发中建议使用。

静态内部类(推荐使用)

代码实现

//静态内部类(线程安全)
public class Lazy {
    private Lazy(){

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

优缺点

  1. 采用了类装载的机制保证了初始化实例时,只有一个线程。
  2. 静态内部类方法在此类被加载时不会立即实例化,只有在调用getInstance()方法时才会加载其内部类并完成此类的实例化。
  3. 类的静态属性只会在第一次加载类时初始化,此处,JVM帮助我们保证了线程的安全性。类初始化时,别的线程无法进入。
  4. 优点:避免了线程不安全,使用静态内部类的特点实现了延迟加载,效率高。
  5. 结论:推荐使用。

枚举(推荐使用)

代码实现

public enum Singleton {
    INSTANCE;//属性
    public void sayOk(){
        System.out.println("ok~~");
    }
}

优缺点

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

注意事项

  1. 单例模式保证了系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能。
  2. 要实例化一个单例类时,使用相应获取对象的方法而不是new();
  3. 单例模式使用场景:需要频繁进行创建或销毁的对象;创建对象时耗时过多或耗费资源过多(重量级对象)且经常使用的对象,工具类对象,频繁访问数据库或文件的对象(如:数据源,session工厂等 )

posted on 2021-07-06 22:10  凡人精灵  阅读(54)  评论(0编辑  收藏  举报

导航