单例设计模式

解决的问题:
    一个应用范围内,一个class类,只应该存在一个对象。
    
单例设计模式的特点:
    1.构造私有(不能通过正常手段进行new对象---因为通过反射还可以私有的方法进行调用)
    2.静态成员变量(存储单例对象的引用)
    3.提供一个公开静态的访问方法,获取单例对象。

两种方式:
    懒汉式:会存在线程安全问题(面试最常问的就是这个)
    饿汉式:不存在线程安全问题
    
饿汉式和懒汉式的区别:
    饿汉式,是不管你用不用该对象,都创建。
        ------会造成资源的浪费。
        ------在创建对象的时候,不会出现多线程情况。
        
    懒汉式,只有第一次访问的时候,才会创建。
        ----也叫延迟加载,由于被加载的时候,可能存在多线程访问,加上被访问的类中有成员变量。
        
单例模式常见的规范写法有多种,我们只讲两种:
    1.双重检查锁(DCL)+ volatile
    2.静态内部类
    
饿汉式的单例对象,什么时候创建?
当单例对象所对应的类被初始化的时候,才会创建该单例对象!!!
类的初始化时机有哪些?通过new Student、通过访问static成员变量。。。

在JVM加载类的时候,由JVM对加载类的线程加锁,
也就是说,类加载的时候,是线程安全的。不会存在多线程安全问题。

如何判断线程安全问题?
    1:是否存在多线程    是
      2:是否有共享数据    是
      3:是否存在非原子性操作    (是针对字节码指令,不是高级语言的代码)
    
UserService{
    private UserDao userDao;
    
    public void method1(){
    
    }
}


原子性的理解:
    new Student();//是否是一个原子性操作呢?
    i++;//是否是一个原子性操作呢?
        1.先取出i变量的值
        2.进行加1操作
        3.将加1之后的值赋值给i
    
如何保证原子性呢?加锁

可见性的理解:
    见图
    
    可见性的解决方案:volatile关键字的一个作用:禁止cpu缓存使用
    
有序性的理解:
    int x ;//step1
    boolean a;//step2
    
    x = 20;//step3
    a = false;//step4
    
    x 和 a之间是没有依赖性(有没有happened-before关系)
    所以说,jvm会考虑性能问题,有可能对step3和step4进行指令重排序。
    
    int x = 10;
    int y ;
    
    y = x + 10;
    
    //此时x和y就存在了依赖性,所以说此处不会发生指令重排序。
    
    指令重排序是不会影响最终的结果的。
    
    有序性的解决,也是通过volatile关键字去解决。
        volatile的另外一个作用就是禁止指令重排序。
    
    

动态代理模式

代理模式解决的问题:
    1.将目标类不擅长的工作交给代理类去做。
    2.对于模板性的代码,可以交给代理模式去做。
    3.为了不修改原有代码的情况下,进行增强目标类的代码功能。
        比如事务、日志统计等相应处理

代理模式的角色:
    目标类
    代理类

代理模式分成两种:静态代理、动态代理
    静态代理是编译时期的代理,其实就是多写一个类去代理目标类

aop织入:动态织入和静态织入
    静态织入就是通过在class文件被加载的时候,
        可以使用一些工具对class文件进行修改(修改的就是代理的代码)
    动态织入就是通过动态代理方式去实现的
    
常用的动态代理方式:
    jdk
    cglib(asm)


jdk:针对带有接口的类进行动态代理
    (目标类和代理类的关系,是同级的,实现了共同的接口)
cglib:非接口的类,使用继承的方式进行代理。
    (目标类和代理类是父子关系,目标类是父类,代理类是子类)

JDK动态代理实现的原理分析
    见图
    1.代理对象如何创建
        public class proxy.target.Proxy$4 implements UserService{

            private InvocationHandler h;
            
            private Method m1;
            
            public void saveUser() {
                this.h.invoke(this,m1,null);
            }
            
            static{
                m1 = Class.forName("proxy.target.UserServiceImpl").getMethod("saveUser",null);
            }

        }

        jvm生成代理对象的过程
        1.写源代码(java代码,JVM编写)----根据接口信息-----InvocationHandler
        2.使用java自带的api,将java代码编译成字节码(可以保存到一个临时地点)
        3.通过Classloader将class文件加载到jvm内存中
        4.创建InvocationHandler对象
        5.根据加载到的class信息去创建代理对象,并将invocationHandler对象传入
                2.代理对象如何执行

小小____
435 声望110 粉丝

哇哈~我在那~这是哪~每日连11问


« 上一篇
Mysql
下一篇 »
Spring