首页 > 其他分享 >单例模式

单例模式

时间:2022-08-30 22:37:11浏览次数:78  
标签:Singleton 模式 instance 线程 private 单例 static public

单例模式是什么?为什么要使用单例模式

单例模式,顾名思义就是在整个运行时域,一个类只有一个实例对象

为什么要用单例呢。因为有的类的,比较庞大和复杂,如果频繁创建和销毁对象,而且这些对象是可复用的话,就会造成不必要的性能浪费。

单例模式的写法?

多种写法,考虑三点:

  1. 是否线程安全

  2. 是否懒加载

  3. 能否反射破坏

public class Singleton {
   //懒加载,线程不安全
   private static Singleton instance = null;
   
   private Singleton() {
  }
   
   public static Singleton getInstance() {
       if (instance == null) {
           instance = new Singleton();
      }
       return instance;
  }
}

上面的单例是线程不安全的,因为在执行if (instance == null)时,可能会有多个线程同时进入,从而实例化多次

那么如果把getInstance()的签名改成:public static synchronized Singleton getInstance() {} 可以吗?

这样确实能线程安全,但是我们只需要对象在构建的时候同步线程,上面时每次获取实例的时候都同步线程,开销极大 。

线程安全问题出现在了构建对象阶段,那么我们只要在编译器构建对象,在运行时调用,就不用考虑线程安全问题了,于是我们这么写:

public class Singleton {
   //编译器构建,线程安全,不是懒加载
   private static Singleton instance = new Singleton();

   private Singleton() {
  }

   public static Singleton getInstance() {
       return instance;
  }
}

那么能不能线程安全和懒加载都能保证呢?

我们是想在构建对象的时候同步,而可以直接使用对象的时候就没必要同步,所以可以这么写:

public class Singleton {
   private static Singleton instance = null;

   private Singleton() {
  }

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

但是这么写会有问题:可能有多个线程进入 if (instance == null) {} 代码块 ,虽然只有一个线程A能拿到锁,但是它一释放,线程B就会立即获取锁进行对象创建,对象就被创建了多次。

可以使用双检锁:

public class Singleton {
   private static Singleton instance = null;

   private Singleton() {
  }

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

但是上面的写法也会遇到问题:

instance = new Singleton() 在指令层面并不是一个原子操作,而是分为三步:

  1. 分配内存

  2. 初始化对象

  3. 对象指向内存地址

在真正执行时,JVM为了执行效率,可能会对指令重排,比如 1 3 2

如果按照这个顺序,A线程执行到了第三步,还未初始化。假设就在此时,线程B执行到了 if (instance == null),false直接跳过,

返回instance导致对象未初始化。

解决办法:给instance加上volatile修饰

public class Singleton {
   //懒加载,线程安全
   //volatile修饰的变量不能被指令重排
   private volatile static Singleton instance = null;

   private Singleton() {
  }

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

那有没有简单一点的写法呢:静态内部类

public class Singleton {
   //懒加载,线程安全
   private static class SingletonHolder {
       private static final Singleton INSTANCE = new Singleton();
  }

   private Singleton() {
  }

   public static Singleton getInstance() {
       return SingletonHolder.INSTANCE;
  }
}

懒加载:静态内部类在程序启动的时候不会加载,只有第一次被调用的时候才会加载。

线程安全:多线程时,类加载机制能实现线程安全,因为loadClass方法使用了synchronized

上述写法都可以被反射破坏,因为反射可以获取到类的构造函数,包括私有构造函数,因此反射可以生成新的对象

单例模式之枚举实现 https://blog.csdn.net/lovelion/article/details/110983839

public enum Singleton {
   INSTANCE;
}

在实现过程中,Java虚拟机会保证枚举类型不能被反射并且构造函数只被执行一次

标签:Singleton,模式,instance,线程,private,单例,static,public
From: https://www.cnblogs.com/WilsonEdwards/p/16641099.html

相关文章

  • C# 装饰模式
    //Seehttps://aka.ms/new-console-templateformoreinformation/**个人理解你要往原有的东西上加东西Phone=装饰手机原材料Decorator=装饰手机中间人Xiao......
  • C嵌入式编程设计模式-C语言类实现方式
    类的封装方式以文件作为封装边界,将外部调用的函数声明,全局变量变量放入头文件中,将具体实现放入.c文件中。简单栈的实现代码:/************************************......
  • js 严格模式
    //js使用严格模式可以规范我们写代码//要启用严格模式,您只需要在JavaScript脚本的开头添加"usestrict";或'usestrict';指令即可,如下所示:<script>"usestrict";......
  • ACM模式输入处理
    字符串输入遇到空格问题cin遇到空格会停止输入一句英文,存入一个字符串使用:getline(cin,s)例如:iamaboygetline是碰到终止符才停止如果你要把一个个单词存成一......
  • FTP的传输有两种方式:ASCII传输模式和二进制数据传输模式
    FTP的传输有两种方式:ASCII传输模式和二进制数据传输模式_boker的博客-CSDN博客_ftp传输二进制 https://blog.csdn.net/z507263441/article/details/38586769FTP的传输有......
  • PageObject(PO)设计模式在 UI 自动化中的实践总结(以 QQ 邮箱登陆为例)
    ⬇️点击“下方链接”,提升测试核心竞争力!>>更多技术文章分享和免费资料领取PO的思想最早是2013年由IT大佬MartinFlower提出的:https://martinfowler.com/bliki/PageObje......
  • mybatis_13_SqlSessionFactory的DCL单例模式
    SqlSessionFactory的DCL单例模式 publicclassSqlSessionFactorySingleton{privateSqlSessionFactorySingleton(){}/***volatile关键字在此......
  • 【设计模式】21.适配器模式
    说明:它是结构型的,类与类之间的桥梁,针对现有类缺什么,做个桥梁,与适合的类用上,直白点的比喻就是三个插头的插座,要加个适配器能插二个插头的。目的:一般主要做二次开发,不会在设......
  • Java之设计模式和设计原则
    一、七大原则1.1、单一职责原则1.2、里氏替换原则1.3、依赖倒置原则1.4、接口隔离原则1.5、迪米特法则1.6、开闭原则1.7、合成复用原则二、设计模式总体来说设计......
  • JAVA进阶--static、工具类、单例、继承--2022年8月28日
    第一节 static静态关键字1、成员变量的分类和访问分别是什么样的?静态成员变量(有static修饰,属于类,加载一次,可以被共享访问)访问格式:类名.变量......