为何Java单例模式我只推荐两种

 更新时间:2019年06月05日 10:28:48   作者:GitCode  
这篇文章主要给大家介绍了关于Java单例模式推荐的两种模式,文中通过示例代码介绍的非常详细,对大家学习或者使用Java具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧

双重检查模式

public class Singleton { 
 private volatile static Singleton singleton; //1:volatile修饰
 private Singleton (){} 
 public static Singleton getSingleton() { 
 if (singleton == null) { //2:减少不要同步,优化性能
  synchronized (Singleton.class) { // 3:同步,线程安全
  if (singleton == null) { 
   singleton = new Singleton(); //4:创建singleton 对象
  } 
  } 
 } 
 return singleton; 
 } 
}

推荐理由:

  1. 延迟初始化。和懒汉模式一致,只有在初次调用静态方法getSingleton,才会初始化signleton实例。
  2. 性能优化。同步会造成性能下降,在同步前通过判读singleton是否初始化,减少不必要的同步开销。
  3. 线程安全。同步创建Singleton对象,同时注意到静态变量singleton使用volatile修饰。

为什么要使用volatile修饰?

虽然已经使用synchronized进行同步,但在第4步创建对象时,会有下面的伪代码:

memory=allocate(); //1:分配内存空间
ctorInstance(); //2:初始化对象
singleton=memory; //3:设置singleton指向刚排序的内存空间

当线程A在执行上面伪代码时,2和3可能会发生重排序,因为重排序并不影响运行结果,还可以提升性能,所以JVM是允许的。如果此时伪代码发生重排序,步骤变为1->3->2,线程A执行到第3步时,线程B调用getsingleton方法,在判断singleton==null时不为null,则返回singleton。但此时singleton并还没初始化完毕,线程B访问的将是个还没初始化完毕的对象。当声明对象的引用为volatile后,伪代码的2、3的重排序在多线程中将被禁止!

静态内部类模式

public class Singleton { 
 private Singleton(){
 }
  public static Singleton getSingleton(){ 
  return Inner.instance; 
 } 
 private static class Inner { 
  private static final Singleton instance = new Singleton(); 
 } 
} 

推荐理由:

  1. 实现代码简洁。和双重检查单例对比,静态内部类单例实现代码真的是太简洁,又清晰明了。
  2. 延迟初始化。调用getSingleton才初始化Singleton对象。
  3. 线程安全。JVM在执行类的初始化阶段,会获得一个可以同步多个线程对同一个类的初始化的锁。

如何实现线程安全?

线程A和线程B同时试图获得Singleton对象的初始化锁,假设线程A获取到了,那么线程B一直等待初始化锁。线程A执行类初始化,就算双重检查模式中伪代码发生了重排序,也不会影响线程A的初始化结果。初始化完后,释放锁。线程B获得初始化锁,发现Singleton对象已经初始化完毕,释放锁,不进行初始化,获得Singleton对象。

在涉及到反射和序列化的单例中,建议使用下文的枚举类型模式。

其他类型的单例模式

懒汉模式(多线程不安全)

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

饿汉单例模式(多线程安全)

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

饿汉模式的线程安全同样通过类加载解决同步问题,但没有达到懒加载目的。(这里非常感谢之初z-chu的指正)

枚举单例模式(多线程安全)

public enum Singleton {
  INSTANCE;
  
  public void doSomething(){
    //todo doSomething
  }
}

在Joshua Bloch大神的《Effective Java》是推荐该方法的。虽然线程安全,在实际开发中,还没有被广泛采用。因为太过简洁以致于可读性较差,还没有在实战中被广泛推广。枚举单例模式的线程安全同样利用静态内部类中讲到类初始化锁。枚举单例模式能够在序列化和反射中保证实例的唯一性。

高手之间的过招,必选择枚举单例模式。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对脚本之家的支持。

相关文章

  • JUnit单元测试入门必看篇

    JUnit单元测试入门必看篇

    下面小编就为大家分享一篇JUnit单元测试入门必看篇,对新手而言有很好的参考价值,希望对大家有所帮助
    2017-11-11
  • JWT Token实现方法及步骤详解

    JWT Token实现方法及步骤详解

    这篇文章主要介绍了JWT Token实现方法及步骤详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-09-09
  • IDEA 离线迁移Springboot工程的方法步骤

    IDEA 离线迁移Springboot工程的方法步骤

    这篇文章主要介绍了IDEA 离线迁移Springboot工程的方法步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-06-06
  • mybatis嵌套循环map方式(高级用法)

    mybatis嵌套循环map方式(高级用法)

    这篇文章主要介绍了mybatis嵌套循环map方式(高级用法),具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-01-01
  • 使用SpringMVC接收文件流上传和表单参数

    使用SpringMVC接收文件流上传和表单参数

    这篇文章主要介绍了使用SpringMVC接收文件流上传和表单参数,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-02-02
  • SpringBoot如何实现word文档转pdf

    SpringBoot如何实现word文档转pdf

    这篇文章主要介绍了SpringBoot如何实现word文档转pdf,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-07-07
  • 如何解决IDEA没有新建servlet选项问题

    如何解决IDEA没有新建servlet选项问题

    这篇文章主要介绍了如何解决IDEA没有新建servlet选项问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2025-04-04
  • Springboot在IDEA热部署的配置方法

    Springboot在IDEA热部署的配置方法

    这篇文章主要介绍了Springboot在IDEA热部署的配置方法,给大家补充介绍了Intellij IDEA 4种配置热部署的方法,需要的朋友可以参考下
    2018-04-04
  • 详解使用Mybatis-plus + velocity模板生成自定义的代码

    详解使用Mybatis-plus + velocity模板生成自定义的代码

    这篇文章主要介绍了详解使用Mybatis-plus + velocity模板生成自定义的代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-03-03
  • Java将一个正整数分解质因数的代码

    Java将一个正整数分解质因数的代码

    这篇文章主要介绍了将一个正整数分解质因数。例如:输入90,打印出90=2*3*3*5,需要的朋友可以参考下
    2017-02-02

最新评论