饿汉式(静态变量)

  • 在类加载的时候就装载完毕,不会出现线程同步的问题
  • 但是一上来就加载不用的话就会造成资源浪费,没有达到懒加载的特性,造成了不必要的内存浪费
  • 结论:这种设计模式可以用,但是会造成资源浪费

    1. public class SingleMode {
    2. private SingleMode() {}
    3. private static final SingleMode Instance = new SingleMode();
    4. public static SingleMode getInstance(){
    5. return Instance;
    6. }
    7. }


    饿汉式(静态代码块)

  • 优缺点和静态变量方式一模一样

    1. public class SingleMode {
    2. private SingleMode() {}
    3. private static SingleMode Instance;
    4. static {
    5. Instance = new SingleMode();
    6. }
    7. public static SingleMode getInstance(){
    8. return Instance;
    9. }
    10. }


    懒汉式(线程不安全)

  • 达到了懒加载的效果,但是线程不安全了

  • 为什么线程不安全? 因为多线程情况下第一个线程走到if逻辑里,第二个线程也进入了,会生成多个实例
  • 结论:多线程情况下不可用,破坏了单例模式,实际开发中最好不要用

    1. public class SingleMode {
    2. private SingleMode(){}
    3. private static SingleMode instance;
    4. public static SingleMode getInstance(){
    5. if (null == instance){
    6. instance = new SingleMode();
    7. }
    8. return instance;
    9. }
    10. }


    懒汉式(线程安全但性能低)

  • 既达到了线程安全,又达到了懒加载,这难道是完美的单例吗?

  • synchronized把getInstance()变成了同步方法,线程会排队,从而达到了线程安全,但是又效率很低
  • 其实我只需要在第一次请求getInstance()的时候同步,instance不为空直接return出去就可以了,但是现在每次都要排队
  • 结论:实际工作不要用,效率极低

    1. public class SingleMode {
    2. private SingleMode(){}
    3. private static SingleMode instance;
    4. public static synchronized SingleMode getInstance(){
    5. if (null == instance){
    6. instance = new SingleMode();
    7. }
    8. return instance;
    9. }
    10. }


    懒汉式(线程安全)

  • 这种写法既保证了线程安全,又保证了性能

  • 双层if检测,如果两个线程运行到了第二个if后来的线程会排队,第一个线程满足了所有条件后new SingleMode();实例然后return出去,第二个线程进去后发现if (null == instance)不为空了,所以就直接return instance
  • volatile 关键字防止多线程造成的指令重排,说白了就是底层执行顺序的保证
  • 结论:无话可说,推荐使用
    1. private SingleMode(){}
    2. private static volatile SingleMode instance;
    3. public static SingleMode getInstance(){
    4. if (null == instance){
    5. synchronized (SingleMode.class){
    6. if (null == instance){
    7. instance = new SingleMode();
    8. }
    9. }
    10. }
    11. return instance;
    12. }
    13. }

懒汉式(线程安全静态内部类)

  • 静态内部类在SingleMode装载的时候不会被装载,只有我们调用getInstance()的时候去装载SingleModes,从而实现SingleMode的实例化,利用静态内部类从而实现了延迟加载
  • 因为static修饰的INSTANCE 会在类装载的时候初始化,类初始化的时候是线程安全的,所以jvm帮助我们完成了一个比较完美的单例
  • 结论:利用静态内部类不会初始装载的特性和static的特性,完成了一个单例模式,应该是最装逼的写法了
    1. public class SingleMode {
    2. private SingleMode(){}
    3. private static class SingleModes{
    4. private static SingleMode INSTANCE = new SingleMode();
    5. }
    6. public static SingleMode getInstance(){
    7. return SingleModes.INSTANCE;
    8. }
    9. }

枚举实现单例

  • 我们定义的一个枚举,在第一次被真正用到的时候,会被虚拟机加载并初始化,而这个初始化过程是线程安全的。而我们知道,解决单例的并发问题,主要解决的就是初始化过程中的线程安全问题。所以,由于枚举的以上特性,枚举实现的单例是天生线程安全的。
  • 总结:按照知乎大神说的,枚举的方式应该是最为推荐使用的
    1. public class SingleMode {
    2. public static void main(String[] args) {
    3. SingleEnum instance = SingleEnum.INSTANCE;
    4. SingleEnum instance1 = SingleEnum.INSTANCE;
    5. System.out.println(instance == instance1);
    6. }
    7. }
    8. enum SingleEnum{
    9. INSTANCE;
    10. }

JDK源码运用单例模式

image.png