• JAVA设计模式学习笔记 单例模式(1)


    单例模式是指使某个类只被实例化一次,其余位置再次调用初始化函数会返回之前生成的第一个实例的引用。

    1、懒汉模式

    延迟加载,用到的时候才加载

    可能会有以下问题

    (1)线程安全问题

    (2)double check加锁优化

    (3)指令重排,需要使用volatile关键字修饰

    class Obj1{//单例类
        private volatile static Obj1 instance;
        private Obj1(){}//私有构造方法避免被主动调用实例化
    
        public static Obj1 getObj1(){//获取实例调用方法
            if(instance == null){// #a
                synchronized (Obj1.class){//锁优化,本来可以直接锁方法,但是会造成性能下降
                    if(instance == null){
                        instance = new Obj1();
                        //JAVA字节码顺序
                        //1、分配空间 2、初始化 3、引用赋值
                        //有的时候2、3顺序可能颠倒,当先执行3时,若其他线程恰好执行到#a处,则会导致返回空指针,因此instance变量需要volatile关键字,保证不会指令重排
                    }
                }
            }
            return instance;
        }
    }
    public class Test1 {
        public static void main(String[] args) {
            //调用测试
            new Thread(() -> {
                Obj1 obj1 = Obj1.getObj1();
                System.out.println(obj1);
            }).start();
            new Thread(() -> {
                Obj1 obj1 = Obj1.getObj1();
                System.out.println(obj1);
            }).start();
        }
    }

    2、饿汉模式

    初始化阶段完成实例的初始化,本质是借助jvm类加载机制

    类加载过程

    (1)加载二进制数据到内存中,生成对应的class数据结构

    (2)连接 a验证 b准备(静态成员变量赋默认值),c解析

    (3)初始化:类的静态变量赋初值

    只有在真正使用该类时才会触发初始化(当前类是启动类,new,访问静态属性,访问静态方法,反射访问,初始化类的子类 等)

    package danli;
    class Obj2{
        private static Obj2 instance = new Obj2();//该类初始化时,instance将被直接赋初始值。同时由于类加载线程安全,不需要考虑多线程影响
        private Obj2(){}
        public static Obj2 getInstance(){
            return instance;
        }
    }
    public class Test2 {
    
        public static void main(String[] args){
            //测试
            Obj2 o1 = Obj2.getInstance();
            Obj2 o2 = Obj2.getInstance();
            System.out.println(o1==o2);
    
            //多线程测试
            new Thread(() -> {
                Obj2 obj2 = Obj2.getInstance();
                System.out.println(obj2);
            }).start();
            new Thread(() -> {
                Obj2 obj2 = Obj2.getInstance();
                System.out.println(obj2);
            }).start();
        }
    }

    3、内部静态类

    依靠jvm的类加载机制(用到时初始化),既保证懒加载,又能线程安全

    class Obj3{
        private Obj3(){}
        private static class InnerObj3{
            private static Obj3 instance = new Obj3();
        }
        public static Obj3 getInstance(){
            return InnerObj3.instance;
        }
    }

    4、反射攻击

    对以上方式进行反射创建测试

    public class Test3 {
        public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
            Constructor<Obj3> declaredConstructor = Obj3.class.getDeclaredConstructor();//获取类的构造函数
            declaredConstructor.setAccessible(true);//变更访问权限
            Obj3 obj3 = declaredConstructor.newInstance();//获得实例
    
            Obj3 obj31 = Obj3.getInstance();
    
            System.out.println(obj31==obj3);//false
        }
    }

    可在单例类中添加一些代码防止产生多例,如内部类中

    class Obj3{
        private Obj3(){
            if(InnerObj3.instance!=null){
                throw new RuntimeException("单例不允许多个实例");
            }
        }
        private static class InnerObj3{
            private static Obj3 instance = new Obj3();
        }
        public static Obj3 getInstance(){
            return InnerObj3.instance;
        }
    }

    懒汉模式不能用这种

  • 相关阅读:
    IT知识构架与操作系统简介
    read与for循环语句
    shell基础练习题
    shell 100以内加法
    shell
    python学习第一日
    shell脚本基本介绍
    bash的基本特性
    Linux习题
    Linux基本命令
  • 原文地址:https://www.cnblogs.com/gaokaitai/p/15007700.html
Copyright © 2020-2023  润新知