欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

23种设计模式之单例模式

程序员文章站 2022-06-04 23:17:13
...

单例模式

Singleton

单例模式就是只有一个实例存在

有时候完全只需要一个实例存在就够了,不用那么多实例存在

饿汉式(常用)

//饿汉式
public class SingLeton01 {
    //类加载的时候就实例化这个单例
    private static final SingLeton01 INSTANCE = new SingLeton01();

    //私有化无参构造,封装好这个类 防止使用者再new而产生多个实例
    private SingLeton01(){

    }

    //拿到这个单例的方法
    public static SingLeton01 getInstance(){
        return INSTANCE;
    }

    public static void main(String[] args) {
        //测试多线程是否安全
        for (int i = 0; i < 100; i++) {
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            new Thread(()-> System.out.println(SingLeton01.getInstance())).start();
        }
    }
}

  • 优点

    • 简单常用

    • 私有化无参构造,让使用者不能再产生多的实例化

    • 类加载到内存时,实例化这个单例

    • 因为类只加载一次,所以只有这个单例,线程安全(JVM只加载一次这个类)

  • 缺点

    • 无论使用与不使用,这个单例在类加载时都被实例化了

懒汉式

//懒汉式 
public class SingLeton02 {
    private SingLeton02(){

    }
    //类加载时 不初始化 所以不加final
    private static   SingLeton02 INSTANCE;

    public static SingLeton02 getInstance(){
        //第一次调用这个方法时,INSTANCE为空实例化对象,之后不为空了就直接返回这个单例
        if (INSTANCE == null){
             /*try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }*/
            INSTANCE = new SingLeton02();
        }
        return INSTANCE;
    }

    public static void main(String[] args) {
        //多线程测试 发现线程不安全(测不出来可以在getInstance方法中线程睡眠) 
        for (int i = 0; i < 100; i++) {
            new Thread(()-> System.out.println(SingLeton02.getInstance())).start();
        }
    }
}
  • 线程不安全原因:

    • 当线程A进入getInstance方法判断INSTANCE为空,此时线程B也进入getInstance方法并判断INSTANCE为空,然后就造成了实例多个对象,并非单例
  • 虽然解决了按需要来进行初始化的问题,但是引来了线程不安全

懒汉式加锁优化

既然懒汉式线程不安全,那就加锁

//懒汉式加锁优化
public class SingLeton03 {
    private SingLeton03(){

    }

    private static SingLeton03 INSTANCE;

    //因为这里是同步方法,所以锁的是SingLeton03这个类
    public synchronized static SingLeton03 getInstance(){
        if (INSTANCE == null){
            INSTANCE = new SingLeton03();
        }
        return INSTANCE;
    }

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

虽然线程安全了,但是因为加了synchronized效率变低了(每次操作都要看是否加了锁等…)

懒汉式同步块再优化

将同步方法变为同步块再优化

//懒汉式同步块优化
public class SingLeton04 {
    private SingLeton04(){

    }

    private static SingLeton04 INSTANCE;


    public  static SingLeton04 getInstance(){
        if (INSTANCE == null){
            synchronized (SingLeton04.class){
                if (INSTANCE == null){
                    INSTANCE = new SingLeton04();
                }

            }
        }
        return INSTANCE;
    }

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

静态内部类方式

//静态内部类 实现懒加载,线程安全
public class SingLeton05 {
    private SingLeton05(){

    }

    //静态内部类
    public static class Inner{
        private static SingLeton05 INSTANCE = new SingLeton05();
    }

    public  static SingLeton05 getInstance(){
            return Inner.INSTANCE;
    }

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

Inner类在SingLeton05类内部,外部访问不了

实现了懒加载和线程安全(JVM保证单例)

枚举单例(完美)

effecfive java 作者提出

public enum SingLeton06 {
    INSTANCE;

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

解决线程同步

防止反序列化

上面的写法:Java通过反射反序列化 Class对象在内存中然后再把实例new出来

枚举的反序列化不是通过反射

枚举默认无参构造私有化

记录学习单例模式,如有错误,请指出,感谢

相关标签: 23种设计模式