设计模式系列(一)单例模式

1.单例模式

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

  • 单例类只能有一个实例。
  • 单例类必须自己创建自己的唯一实例。
  • 单例类必须给所有其他对象提供这一实例。

意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
主要解决:一个全局使用的类频繁地创建与销毁。
何时使用:当您想控制实例数目,节省系统资源的时候。
如何解决:判断系统是否已经有这个单例,如果有则返回,如果没有则创建。
关键代码:构造函数是私有的。

优点
  1. 在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。
  2. 避免对资源的多重占用(比如写文件操作)。
缺点

没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。

使用场景:
  1. 要求生产唯一序列号。
  2. WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。
  3. 创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。

注意事项:getInstance() 方法中需要使用同步锁 synchronized (Singleton.class)
防止多线程同时进入造成 instance 被多次实例化。

  • 饿汉式 静态常量
//饿汉式(静态常亮)在类装载的时候完成了实例化,避免了线程同步问题。但是没有达到懒加载,有可能造成内存的浪费。
class Singleton {
    //构造私有化
    private Singleton() {

    }

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

    //获取实例
    public static Singleton getInstance() {
        return instance;
    }
}
  • 饿汉式 静态代码块
//优缺点同上
class Singleton {
    //构造私有化
    private Singleton() {

    }

    //内部创建对象实例
    private static Singleton instance;

    static {
        instance = new Singleton();
    }

    //获取实例
    public static Singleton getInstance() {
        return instance;
    }
}
  • 懒汉式 线程不安全

class Singleton {
    //构造私有化
    private Singleton() {

    }

    //内部创建对象实例
    private static Singleton instance;

    //获取实例
    public static Singleton getInstance() {
        //在使用的时候才去创建(此处会出现线程不安全问题)
        if (instance == null)
            instance = new Singleton();
        return instance;
    }
}
  • 懒汉式 线程安全 同步方法

class Singleton {
    //构造私有化
    private Singleton() {

    }

    //内部创建对象实例
    private static Singleton instance;

    //获取实例    synchronized让线程依次获取instance
    public static synchronized Singleton getInstance() {
        if (instance == null)
            instance = new Singleton();
        return instance;
    }
}
  • 懒汉式 线程安全 同步方法
//虽然可以解决线程不安全问题,但是每次获取instance都走同步方法,会导致效率低
class Singleton {
    //构造私有化
    private Singleton() {

    }

    //内部创建对象实例
    private static Singleton instance;

    //获取实例    synchronized让线程依次获取instance
    public static synchronized Singleton getInstance() {
        if (instance == null)
            instance = new Singleton();
        return instance;
    }
}
  • 懒汉式 线程安全 同步代码块(双重判断) volatile修饰变量
//volatile 共享变量 可以在修改后立刻刷新
class Singleton {
    //构造私有化
    private Singleton() {

    }

    //内部创建对象实例
    private static volatile Singleton instance;

    //获取实例    synchronized让线程依次获取instance
    public static synchronized Singleton getInstance() {
        if (instance == null)
            //没有作用 因为已经进入if语句了
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        return instance;
    }
}
  • 静态内部类 (外部类被装载的时候,内部类不会被装载)

class Singleton {
    //构造私有化
    private Singleton() {

    }

    //静态内部类,该类中有一个静态的属性,静态内部类只有被调用的时候,才回装载(此处相当于懒加载,可以节省内存) 在类装载的时候,是线程安全的。 而且类只装载一次
    private static class SingletonInstance {
        private static final Singleton instance = new Singleton();
    }

    //获取实例    synchronized让线程依次获取instance
    public static synchronized Singleton getInstance() {
        return SingletonInstance.instance;
    }
}

-枚举方式 避免多线程同步问题,防止反序列化重新创建对象

enum Singleton {
    INSTANCE;

    public void method() {
        System.out.println("单例调用");
    }
}
单例模式在jdk中的应用

Runtime对象,用到了饿汉式的单例

如何破坏单例,如何防止
  • 通过反射破坏单例
    虽然构造方法以及被私有化,但还是可以通过反射产生新的对象。

          //通过反射获取
          Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor();
          constructor.setAccessible(true);
          Singleton reflex = constructor.newInstance();
          System.out.println("reflex的hashCode:"+reflex.hashCode());
    

    如何防止反射破坏
    创建一个全局变量设为true,在构造方法中判断,如果是一个次创建,就把变量设为false,如果不是,就抛出异常。

      class Singleton {
        private static boolean isFristCreate = true;//默认是第一次创建
      
        //构造私有化
        private Singleton() {
          if (isFristCreate) {
            synchronized (Singleton.class) {
              if (isFristCreate) {
                isFristCreate = false;
              }
            }
          } else {
            throw new RuntimeException("已然被实例化一次,不能在实例化");
          }
        }
      
        //静态内部类,该类中有一个静态的属性,静态内部类只有被调用的时候,才回装载(此处相当于懒加载,可以节省内存) 在类装载的时候,是线程安全的。 而且类只装载一次
        private static class SingletonInstance {
          private static final Singleton instance = new Singleton();
        }
      
        //获取实例    synchronized让线程依次获取instance
        public static synchronized Singleton getInstance() {
          return SingletonInstance.instance;
        }
      }
    
  • 通过克隆方式破坏

  • 通过反序列化方式破坏

posted @ 2021-01-07 14:32  super_胡  阅读(90)  评论(0编辑  收藏  举报