设计模式 — 单例模式singleton — 创建型

  • Post author:
  • Post category:其他



应用场景

只需要一个实例,在代码和内存方面只希望它有一个实例出来


关键点

第一:构造方法设计成私有的

无法new 出来

第二:提供获取实例的方法


相关概念

1.class.forName(“”)可以加载类名,静态变量load到内存,就会实例化

2.哈希码相同也不一定是同一个对象

3.final修饰的变量 必须要初始化


写法相关


饿汉式


优点:

类加载到内存 就实例化一个单利 jvm保证线程安全,简单实用

缺点:不管用到与否,类装载时就完成了实例化


代码块

public class Single1 {
    private static final Single1 INSTANCE = new Single1();

    private  Single1() {};

    public static Single1 getINSTANCE() {
        return INSTANCE;
    }

    public void m() {System.out.println("m");}

    public static void main(String[] args) {
        Single1 m1 = Single1.getINSTANCE();
        Single1 m2 = Single1.getINSTANCE();
        System.out.println(m1 == m2);
    }
}


懒汉式

优点:达到了按需加载的目的

缺点:带来了线程不安全的问题


代码块

public class Single2 {
    //如果加 final 必须要初始化
    private static Single2 INSTANCE;

    private Single2() {}

    public static Single2 getINSTANCE() {
        if (INSTANCE == null) {
            try {
                //睡一毫秒 都会产生对象的不同的实例化实例
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            INSTANCE = new Single2();
        }
        return INSTANCE;
    }

    public void m() {
        System.out.println("m");
    }

    public static void main(String[] args) {
        for (int i=0; i<100; i++) {
            new Thread(()-> {
                System.out.println(Single2.getINSTANCE().hashCode());
                //不同对象的哈希码是不同的
            }).start();
        }
    }
}

以下几个代码段是进阶版的饿汉式

加上synchronized 锁定了当前语句块,但是效率下降


代码块

public class Single3 {
    //如果加 final 必须要初始化
    private static Single3 INSTANCE;

    private Single3() {}

    //每次都加锁 效率就降低了
    public static synchronized Single3 getINSTANCE() {
        if (INSTANCE == null) {
            try {
                //睡一毫秒 都会产生对象的不同的实例化实例
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            INSTANCE = new Single3();
        }
        return INSTANCE;
    }

    public void m() {
        System.out.println("m");
    }

    public static void main(String[] args) {
        for (int i=0; i<100; i++) {
            new Thread(()-> {
                System.out.println(Single3.getINSTANCE().hashCode());
                //不同对象的哈希码是不同的
            }).start();
        }
    }
}

通过减小同步代码块的方式提高效率,多线程情况下也不能做到同一个实例


代码块

public class Single4 {
    //如果加 final 必须要初始化
    private static Single4 INSTANCE;

    private Single4() {}

    //每次都加锁 效率就降低了
    public static Single4 getINSTANCE() {
        if (INSTANCE == null) {
            synchronized (Single4.class) {
                try {
                    //睡一毫秒 都会产生对象的不同的实例化实例
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                INSTANCE = new Single4();
            }
        }
        return INSTANCE;
    }

    public void m() {
        System.out.println("m");
    }

    public static void main(String[] args) {
        for (int i=0; i<100; i++) {
            new Thread(()-> {
                System.out.println(Single4.getINSTANCE().hashCode());
                //不同对象的哈希码是不同的
            }).start();
        }
    }
}

双重检查单例判断


代码块

public class Single5 {
    //如果加 final 必须要初始化 volatile 禁止指令重排
    private static volatile Single5 INSTANCE;

    private Single5() {}

    //每次都加锁 效率就降低了
    public static Single5 getINSTANCE() {
        if (INSTANCE == null) {
            synchronized (Single5.class) {
                //双重检查
                if (INSTANCE == null) {
                    try {
                        //睡一毫秒 都会产生对象的不同的实例化实例
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    INSTANCE = new Single5();
                }

            }
        }
        return INSTANCE;
    }

    public void m() {
        System.out.println("m");
    }

    public static void main(String[] args) {
        for (int i=0; i<100; i++) {
            new Thread(()-> {
                System.out.println(Single5.getINSTANCE().hashCode());
                //不同对象的哈希码是不同的
            }).start();
        }
    }
}


静态内部类方式

相关知识点:

1.jvm保证单例,加载外部类时不会加载内部类,可以实现懒加载

2.私有化,只有内部类才可以new出来、

3.内部类不会被随便加载,被调用时才会进行加载

4.虚拟机加载一个class的时候,只加载一次


代码块

public class Single6 {
    private Single6() {}

    private static class Single6Holder {
        private final static Single6 INSTANCE = new Single6();
    }

    public static Single6 getInstance() {
        return Single6Holder.INSTANCE;
    }

    public void m() {
        System.out.println("m");
    }

    public static void main(String[] args) {
        for (int i=0; i<100; i++) {
            new Thread(()->{
                System.out.println(Single6.getInstance().hashCode());
            }).start();
        }

    }
}


枚举类方式


相关知识点:


1.不仅可以解决线程同步,还可以防止反序列化


2.单例 防止反序列化,把类反射,load到内存


3.不会被反序列化的原因是:枚举类没有构造方法,就算是反序列化只会拿到枚举类的值


4.语法上是最完美的


代码块

public enum Single7 {

    INSTANCE;

    public void m() {}

    public static void main(String[] args) {
        for (int i=0;i<100; i++) {
            new Thread(()->{
                System.out.println(Single7.INSTANCE.hashCode());
            }).start();
        }
    }
}

相关

spring框架中的bean工厂来保证单例



版权声明:本文为weixin_38738277原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。