设计模式--单例模式

 

单例模式

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

场景:重量级的对象,不需要多个实例,如线程池,数据库连接池

1) 懒汉模式:延迟加载,只有在真正使用的时候,才开始实例化

  a.线程安全问题

  b.Double check 加锁优化

  c.编译器(JIT),CPU有可能对指令进行重排序,导致使用到尚未初始化的实例,可以通过添加volatile关键字进行修饰,对于volatile修饰的字段可以防止指令重排。

  代码:

package com.offcn.designpattern.lazysingleton;
//单例模式-懒汉模式
public class LazysingletonDemo {
    public static void main(String[] args) {
        new Thread(()->{
            LazySingleton instance = LazySingleton.getInstance();
            System.out.println(instance);
        }).start();
        new Thread(()->{
            LazySingleton instance = LazySingleton.getInstance();
            System.out.println(instance);
        }).start();
    }
}
class LazySingleton{
    private volatile static LazySingleton instance;
    private LazySingleton(){
   
 //是为了避免反射创建其他实例
    if(instance != null){
      throw RuntimeException("单例不允许创建多个实例")
    }
  }
    public static LazySingleton getInstance(){
        if(instance == null){
            synchronized (LazySingleton.class){
                if(instance == null){
                    instance = new LazySingleton();
                    //字节码层,2.3顺序可以调,但是可能会引发空指针,所以用volatile
                    //1.分配空间
                    //2.初始化
                    //3.引用赋值
                }
            }
        }
        return instance;
    }
}

输出:

 

2)饿汉模式:类加载的初始化阶段就完成了实例的初始化,本质上就是借助于jvm类的加载机制,保证实例的唯一性。

类加载过程:

  a.加载二进制数据到内存中,生成对应的Class数据结构。

  b.连接:A验证B准备(给类的静态成员变量赋默认值)C解析

  c.初始化:给类的静态变量赋初值

package com.offcn.designpattern.lazysingleton;

public class HungarysingletonDemo {
    public static void main(String[] args) {
        HungarySingleton instance = HungarySingleton.getInstance();
        HungarySingleton instance1 = HungarySingleton.getInstance();
        System.out.println(instance == instance1);
    }
}
class HungarySingleton{
    private static HungarySingleton instance = new HungarySingleton();
    private HungarySingleton(){}
    public static HungarySingleton getInstance(){
        return instance;
    }

}

3)  静态内部类

  a.本质上是利用类的加载机制来保证线程安全

  b.只有在实际使用的时候才会出发类的初始化,所以也是懒加载的一种形式

package com.offcn.designpattern.lazysingleton;

public class InnerclassSingletonDemo {
    public static void main(String[] args) {
        new Thread(()->{
            InnerclassSingleton instance = InnerclassSingleton.getInstance();
            System.out.println(instance);
        }).start();
        new Thread(()->{
            InnerclassSingleton instance1 = InnerclassSingleton.getInstance();
            System.out.println(instance1);
        }).start();
    }
}
//静态内部类
class InnerclassSingleton{
    private static class InnerclassHolder{
        private static InnerclassSingleton instance = new InnerclassSingleton();
    }
    private InnerclassSingleton(){
    //是为了避免反射创建其他实例
    if(intance != null){
      throw RuntimeException("单例模式不允许创建多个实例");
    }
  }
public static InnerclassSingleton getInstance(){ return InnerclassHolder.instance; } }

输出:

 

 4) 使用反射攻击懒加载式单例模式

package com.offcn.designpattern.lazysingleton;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class InnerclassSingletonDemo {
    public static void main(String[] args)throws Exception {
        //通过反射得到调用构造方法创建实例
        Constructor<InnerclassSingleton> declaredConstructor = InnerclassSingleton.class.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        InnerclassSingleton innerclassSingleton = declaredConstructor.newInstance();

        //通过静态内部类得到实例
        InnerclassSingleton instance = InnerclassSingleton.getInstance();
        //判断得到的是否是相同实例,结果返回false
        System.out.println(innerclassSingleton == instance);
        
    }
}
//静态内部类
class InnerclassSingleton{
    private static class InnerclassHolder{
        private static InnerclassSingleton instance = new InnerclassSingleton();
    }
    private InnerclassSingleton(){}
    public static InnerclassSingleton getInstance(){
        return InnerclassHolder.instance;
    }
}

解决方法:

 

 5 )  使用序列化攻击单例模式(因为序列化会调用流来创建实例)

package com.offcn.designpattern.lazysingleton;import ,java.io.*;
import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException;
public class InnerclassSingletonDemo { public static void main(String[] args)throws Exception { InnerclassSingleton instance = InnerclassSingleton.getInstance(); //序列化 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("innerclassSingleton")); oos.writeObject(instance); oos.close(); //反序列化 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("innerclassSingleton")); InnerclassSingleton instance1 = (InnerclassSingleton) ois.readObject();
      
     //返回false System.
out.println(instance == instance1); } } //静态内部类 class InnerclassSingleton implements Serializable { private static class InnerclassHolder{ private static InnerclassSingleton instance = new InnerclassSingleton(); } private InnerclassSingleton(){} public static InnerclassSingleton getInstance(){ return InnerclassHolder.instance; } }

解决方法:

class InnerclassSingleton implements Serializable {
   //固定版本号
static final long serialVersionUID = 42L; private static class InnerclassHolder{ private static InnerclassSingleton instance = new InnerclassSingleton(); } private InnerclassSingleton(){} public static InnerclassSingleton getInstance(){ return InnerclassHolder.instance; } Object readResolve()throws ObjectStreamException{ return InnerclassHolder.instance; } }

6 ) 枚举创建单例

package com.offcn.designpattern.lazysingleton;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public enum EnumSingleton {
    INSTANCE;
}
class EnumTest{
    public static void main(String[] args) throws IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchMethodException {
        EnumSingleton instance = EnumSingleton.INSTANCE;
        EnumSingleton instance1 = EnumSingleton.INSTANCE;
        //返回true
        System.out.println(instance == instance1);
        
        
        
        //  反射不能在枚举类型使用,会报异常
        Constructor<EnumSingleton> declaredConstructor = EnumSingleton.class.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        EnumSingleton enumSingleton =  declaredConstructor.newInstance("INSTANCE",0);
    }
}

反射在枚举中使用时会报异常:

 

 序列化在枚举中使用不会创建多个实例

public enum EnumSingleton {
    INSTANCE;
}
class EnumTest{
    public static void main(String[] args) throws IllegalAccessException, InvocationTargetException, 
    InstantiationException, NoSuchMethodException, IOException, ClassNotFoundException { EnumSingleton instance
= EnumSingleton.INSTANCE; //序列化 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("enumsingleton")); oos.writeObject(instance); oos.close(); //反序列化 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("enumsingleton")); EnumSingleton instance1 = (EnumSingleton) ois.readObject(); //在枚举中使用序列化,不会创建多个实例 //返回true System.out.println(instance == instance1); } }

 

posted @ 2020-02-15 11:36  白白3535  阅读(85)  评论(0)    收藏  举报