• 设计模式--单例模式


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

    应用场景:重量级的对象,不需要多个实例,如线程池,数据库连接池。就是被复用的。。

    懒汉模式,饿汉模式,静态内部类,反射攻击实例,枚举,序列化

    懒汉:延迟加载,

    public class LazySingletonTest {
    public static void main(String[] args) {
    //单线程
    // LazySingleton instance=LazySingleton.getInstance();
    // LazySingleton instance1=LazySingleton.getInstance();
    // System.out.println(instance==instance1);
    //两个线程
    new Thread(()->{
    LazySingleton instance=LazySingleton.getInstance();
    System.out.println("instance = " + instance);
    }).start();
    new Thread(()->{
    LazySingleton instance=LazySingleton.getInstance();
    System.out.println("instance = " + instance);
    }).start();
    }
    /**instance = danli.LazySingleton@437d73bc
    instance = danli.LazySingleton@2ea7cb0b
    这是加synchronize之前,加上之后,只会出现相同的结果*/
    }
    class LazySingleton {
    private volatile static LazySingleton instance;
    private LazySingleton(){

    }
    //提供全局的访问点
    //当调用instance的时候,才会实例化
    public synchronized static LazySingleton getInstance() {
    if (instance == null){
    try {
    Thread.sleep(200);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }

    instance=new LazySingleton();
    }
    return instance;
    }
    }
    加锁的作用,就i是为了保证只new 一个实例,但是加syn就是不管有没有初始化都会加锁,但是当instance!=null的时候
    是不需要加锁的,直接返回

    if (instance == null){
    //但是当两个线程都没有实例的时候,就会都进去实例化,
    // 所以就是两个线程都会实例化,所以要再判断一下
    synchronized (LazySingleton.class){
    if (instance == null) {
    instance=new LazySingleton();
    }
    }
    }
    return instance;
    new 会在堆空间,开辟一块空间。CPU会有即时编码,有指令重拍。1、分配空间2、初始化3、引用赋值。2,3可以倒,但是不影响运行
    volatile会保证Java命令顺序执行,不会出现指令重排现象。
     private volatile static LazySingleton instance;

     延迟加载,在使用的时候,进行加载。要注意的情况:

    1)多线程情况下,线程安全问题,可以syn解决一些

    2)double check 加锁进行优化

    3)还要防止Java的即时编码(JIT),CPU有可能出现的指令重排,导致使用到没有初始化的实例,加volatile关键字。

    饿汉模式---

    public class HungrySingletonTest {
    public static void main(String[] args) {
    //new HungrySingleton();这样不能保证单例
    HungrySingleton instance=HungrySingleton.getInstance();
    HungrySingleton instance1=HungrySingleton.getInstance();
    System.out.println(instance==instance1);
    }
    }
    //饿汉模式
    // 主要是通过类加载机制,
    /**1、加载二进制数据到内存,生成对应的class数据结构
    * 2、连接:a.验证b.准备(给类的静态成员变量赋默认值)c.解析
    * 3、初始化:给类的静态变量赋初值
    * 在类加载的时候,是在类被调用的时候,*/
    class HungrySingleton{

    private static HungrySingleton instance=new HungrySingleton();
    //私有构造函数,保证在外部不能实例化,只要是为了保证单例
    private HungrySingleton() {
    }
    //提供公开的方法,以便访问
    public static HungrySingleton getInstance(){
    return instance;
    }
    }
    静态内部类:
    public class InnerClassSingletonTest {
    public static void main(String[] args) {
    // InnerClassSingleton instance=InnerClassSingleton.getInstance();
    // InnerClassSingleton instance1=InnerClassSingleton.getInstance();
    // System.out.println(instance==instance1);
    //多线程
    new Thread(()->{
    InnerClassSingleton instance=InnerClassSingleton.getInstance();
    System.out.println("instance = " + instance);
    }).start();
    new Thread(()->{
    InnerClassSingleton instance=InnerClassSingleton.getInstance();
    System.out.println("instance = " + instance);
    }).start();
    }
    }
    class InnerClassSingleton{
    private static class InnerClassHolder {
    /**在静态内部类进行属性的初始化,也算是懒加载,不使用的时候,不会实例化*/
    private static InnerClassSingleton instance=new InnerClassSingleton();
    }
    /**提供私有的构造函数,就能保证别人不能再外部访问*/
    private InnerClassSingleton(){
    }
    public static InnerClassSingleton getInstance(){
    return InnerClassHolder.instance;
    }
    }
    原理:
    1)本质上是利用类的加载机制来保证线程安全
    2)只有在实际中使用的时候,才会触发类的初始化,所以也是一种懒加载。
    反射攻击实例:
     public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
    //反射,,,静态,还有懒汉模式
    //这是拿到构造函数
    Constructor<InnerClassSingleton> declaredConstructor = InnerClassSingleton.class.getDeclaredConstructor();
    //这里是拿到访问权,就算是private修饰也可以取到
    declaredConstructor.setAccessible(true);
    //然后进行实例化
    InnerClassSingleton innerClassSingleton = declaredConstructor.newInstance();

    InnerClassSingleton instance = InnerClassSingleton.getInstance();
    //这里的结果是false。
    System.out.println(instance==innerClassSingleton);
    }
    }
    静态和饿汉都可以被反射进行攻击,所以要加防护,,,,懒汉不能保证
    /**提供私有的构造函数,就能保证别人不能再外部访问*/
    private InnerClassSingleton(){
    if (InnerClassHolder.instance!=null){
    throw new RuntimeException("报错,instance已经初始化,单例不允许多个实例");
    }
    }
    保证其他地方不能用反射的方式进行多例的实例化。


    枚举举例:
    1)天然不支持反射创建对应的实例,且有自己的反序列化机制
    2)利用类加载机制保证线程安全
    public enum  EnumSingleton {
    INSTANCE;
    public void print(){
    System.out.println(this.hashCode());
    }
    }
    class EnumTest{
    public static void main(String[] args) {
    EnumSingleton instance=EnumSingleton.INSTANCE;
    EnumSingleton instance1=EnumSingleton.INSTANCE;
    //结果为true
    System.out.println(instance== instance1);
    }
    }

    //可以设置参数,一个string类型,一个int类型
    Constructor<EnumSingleton> declaredConstructor = EnumSingleton.class.getDeclaredConstructor(String.class,int.class);
    //这里进行访问权设置,private修饰的都可以
    declaredConstructor.setAccessible(true);
    //为什么设置这两个参数,底层要继承Enum抽象类(里面的参数为name,int),
    EnumSingleton instance = declaredConstructor.newInstance("INSTANCE", 0);
    这个方法报错,不支持enum类型的对象创建。所以用enum是可以进行单例的创建,不会被反射使用,也是线程安全的


    序列化:
    implement Serializable,,
    当用流进行时,writeOutFile和ObijectInputStream得到的结果并不是一样的。可以加版本号,然后就可以控制内容保持一致。

  • 相关阅读:
    CentOS7通过 yum安装路径查询方法
    centos中如何查看tomcat的版本
    CentOS下安装Filezilla软件
    关于jFinal开发中遇到的中文乱码问题解决办法
    jFinal render为什么不跳转到指定的页面
    [Algorithm] 1290. Convert Binary Number in a Linked List to Integer
    一道百度面试题
    ORA-01078, LRM-00123错误处理
    hdu 4115 (2—SAT)
    【PAT Advanced Level】1004. Counting Leaves (30)
  • 原文地址:https://www.cnblogs.com/xiangyucc/p/13710487.html
Copyright © 2020-2023  润新知