单例模式 懒汉式,饿汉式,内部类,单例,双重校验锁

 

定义:保证一个类仅有一个实例,并且提供一个全局访问点。

饿汉式:类加载的时候完成实例化,保证了线程的安全,但是没有用就造成内存浪费

public class HungrySingleton {
    private final static HungrySingleton hungrySingleton = new HungrySingleton();
    private HungrySingleton(){}
    public static HungrySingleton getInstance(){
        return hungrySingleton;
    }
}

懒汉式:用的时候创建,因为可能同时有两个线程会进入if判断中,有线程安全问题

public class LazySingleton {

    private static LazySingleton lazySingleton = null;
    // 私有化构造器
    private LazySingleton(){};
    public static LazySingleton getInstance(){
        if (lazySingleton == null){
            // 使用的时候创建
            lazySingleton = new LazySingleton();
        }
        return lazySingleton;
    }
}

进行测试:

// 进行测试
public class LazySingleton {
    private static LazySingleton lazySingleton;
    private LazySingleton(){}
    public static LazySingleton getLazySingleton(){
        if (lazySingleton == null){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            lazySingleton = new LazySingleton();
        }
        return lazySingleton;
    }
    public static void main(String[] args) {

        new Thread(new Runnable() {
            @Override
            public void run() {
                LazySingleton lazySingleton = LazySingleton.getLazySingleton();
                System.out.println(lazySingleton);
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                LazySingleton lazySingleton = LazySingleton.getLazySingleton();
                System.out.println(lazySingleton);
            }
        }).start();
    }
}

运行后,输出两个实例,有线程安全问题

单例模式 懒汉式,饿汉式,内部类,单例,双重校验锁_第1张图片

 

保证线程安全的饿汉示写法:

1. 在方法上直接加synchroized锁

public class LazySingleton {

    private static LazySingleton lazySingleton = null;
    // 私有化构造器
    private LazySingleton(){};
    // synchronized 保证线程安全
    public static synchronized LazySingleton getInstance(){
        if (lazySingleton == null){
            // 使用的时候创建
            lazySingleton = new LazySingleton();
        }
        return lazySingleton;
    }
}
在静态方法上锁的是类的Class文件,效率很低,
其实我们只要锁住 lazySingleton = new LazySingleton();这一行就够了

 

2,双重检查加锁方法

public class DoucheckSingleton {
    // volatile 禁止指令重排序
    private static volatile DoucheckSingleton doucheckSingleton;
    private DoucheckSingleton(){}
    public DoucheckSingleton getDoucheckSingleton(){
        // 第一个if判断对象不为null,就不向下走,减少 synchronized 带来的性能开销
        if (doucheckSingleton == null){
            //多个线程同时执行到条件判断语句时,当一个线程创建一个实例之后,singleton就不再为空了,所以这里还需要判断一次
            synchronized (DoucheckSingleton.class){
               if (doucheckSingleton == null){
                   // 1.给对象分配内存空间
                   // 2.调用构造函数,初始化成员变量
                   // 3.将对象指向分配的内存空间
                   // jvm可能先执行3,后执行2,这样就可能造成对象没有完全初始化,volatile就可以禁止指令重排序
                   doucheckSingleton = new DoucheckSingleton();
               }
            }
        }
        return doucheckSingleton;
    }
}

静态内部类

public class StaticInnerClassSinglton {
    private static class InterClass{
        private static StaticInnerClassSinglton staticInnerClassSinglton = new StaticInnerClassSinglton();
    }
    
    public static StaticInnerClassSinglton getInstance(){
        return InterClass.staticInnerClassSinglton;
    }
}

内部静态类和双重检验也不是绝对线程安全的,可能会被反射攻击和序列化

public class Test {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        // 获取InnerClassSingleton的构造器
        Constructor declaredConstructor = StaticInnerClassSinglton.class.getDeclaredConstructor();
        // 设置访问权限
        declaredConstructor.setAccessible(true);
        // 获取反射的实例
        StaticInnerClassSinglton instance = declaredConstructor.newInstance();
        // 通过静态内部类获取实例
        StaticInnerClassSinglton innerClassSingleton = StaticInnerClassSinglton.getInstance();
        // 判断是否是同一个实例
        System.out.println(instance == innerClassSingleton); //输出结果:false 证明是两个实例
    }
}

使用枚举创建:

public enum EnumSingleton {
    INSTANCE;
    private void print(){
        System.out.println(this.hashCode());
    }

    // 进行测试
    public static void main(String[] args) {
        EnumSingleton instance = EnumSingleton.INSTANCE;
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        EnumSingleton instance1 = EnumSingleton.INSTANCE;
        System.out.println(instance == instance1);
        instance.print();
        instance1.print();
    }
}

输出结果为true,表示是同一个实例,也是线程安全的

进行反射攻击测试:

public class EnumTest{
    public static void main(String[] args) throws Exception {
        // 获取InnerClassSingleton的构造器
        Constructor declaredConstructor = EnumSingleton.class.getDeclaredConstructor(String.class,int.class);
        // 设置访问权限
        declaredConstructor.setAccessible(true);
        // 获取反射的实例
        EnumSingleton instance = declaredConstructor.newInstance("INSTANCE",0);
        EnumSingleton instance1 = EnumSingleton.INSTANCE;
        // 直接抛出异常 java.lang.IllegalArgumentException: Cannot reflectively create enum objects
        System.out.println(instance == instance1); 
    }

Cannot reflectively create enum objects,看到枚举类是不允许通过反射进行实例化的。

枚举也不允许反序列化操作,所以枚举是单例模式的最佳实现方法。

你可能感兴趣的:(设计模式)