2020-09-16 个人学习记录(更新githun代码,单例,linux命令,动态代理)

今日计划

1.设计模式—复习下单例
2.Java动态代理

git将本地代码更新到github仓库

  1. git clone https://github.com/Suddee/zhj_design_patterns.git
  2. git add .
  3. git commit -m “”
  4. git push -u origin master(分支)

今天学到的linux命令

  1. grep error * 查看目录下所有文件的错误信息,检测日志的时候很好用
  2. 在libmkl_intel_lp64.so: cannot open shared object file: No such file or directory:
    export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/root/anaconda3/lib/(find找一下)
    3.head -n 1000 a.txt > b.txt 将a的前1000条保存在b中
    4.wc -l a.txt 看a有多少行
    5.sar -u 看cpu利用率,
    使用命令行 sar -u 1 1000(可省略)

在这里插入图片描述
6.tar -xvf 文件.tar 解压tar
tar -cvf 打包后的文件名.tar 要打包的文件

sar -u -o zhou 60 5

计划1–单例模式

已更新到github

1.为什么双重校验锁需要加volatile?
学习自添加链接描述
第一方面:
因为Singleton singleton = new Singleton() 在Jvm中分为三步:
1)为singleton分配内存空间
2)初始化singleton
3)将singleton指向分配的内存空间
但是JVM具有指令重排的特点(在单线程无所谓),执行顺序可能为1-3-2,此时Thread-1执行完了1和3,还没有初始化singleton,此时Thread-2发现this.singleton不为空,直接返回,但是此时singleton并没有完成初始化,导致使用此实例时可能会出现异常,类似于空指针。而使用 volatile 会禁止JVM指令重排,从而保证在多线程下也能正常执行。

第二方面:
volatile关键字保证了变量的可见性,他有俩作用:
1)被volatile关键字修饰的变量,在各自工作内存修改后会立即更新回主内存。
2)在其他工作内存要使用此变量时,会先从主内存更新此变量后再使用。
因此Thread-1在获得锁并创建成功singleton后释放锁,Thread-2等待在锁处,当该线程获得到锁后进入同步代码块后,再判断singleton就不为空了,可以直接返回。

2.为什么说大部分饿汉模式是线程安全的?
在类的加载过程的初始化阶段,初始化阶段是执行类构造器() 方法的过程。

< clinit>() 方法是由编译器自动收集类中的所有类变量(static)的赋值动作和静态语句块(static{})块中的语句合并产生的。因此,private static Singleton singleton = new Singleton();也会被放入到这个方法中。

虚拟机会保证一个类的< clinit>()方法在多线程环境中被正确的加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的< clinit>()方法,其他线程都需要阻塞等待,直到活动线程执行< clinit>()方法完毕。需要注意的是,其他线程虽然会被阻塞,但如果执行< clinit>()方法的那条线程退出< clinit>()方法后,其他线程唤醒后不会再次进入< clinit>()方法。同一个类加载器下,一个类型只会初始化一次。

3.为什么枚举是线程安全的?
我们对如下enum进行反编译可以得到(从.class变成.java)
在这里插入图片描述
在这里插入图片描述
由第二个问题我们可以得知,根据类加载过程可以知道虚拟机会保证一个类的< clinit>() 方法在多线程环境中被正确的加锁、同步。所以,枚举实现是在实例化时是线程安全。

4.为什么枚举是单例?
enum中隐藏了一个私有的构造方法,所以他是单例。

5.为什么静态内部类单例模式是线程安全的?
问题2的答案

计划2----Java动态代理

学习自Java动态代理
cglib代理
使用代码:D:\Java_code\jdk_proxy

动态代理的作用?
动态代理在Java中有着广泛的应用,比如Spring AOP、Hibernate数据查询、测试框架的后端mock、RPC远程调用、Java注解对象获取、日志、用户鉴权、全局性异常处理、性能监控,甚至事务处理等。

常见的两种java动态代理方法?
jdk动态代理,cglib动态代理

代理模式就是给一个对象提供一个代理,并由代理对象来控制对真实对象的访问
动态代理的源码是在程序运行期间由JVM根据反射等机制动态的生成,所以在运行前并不存在代理类的字节码文件。

编写一个调用逻辑处理器 LogHandler 类,提供日志增强功能,并实现 InvocationHandler 接口;在 LogHandler 中维护一个目标对象,这个对象是被代理的对象(真实主题角色);在 invoke 方法中编写方法调用的逻辑处理。

使用代理时:

  1. 创建被代理的对象,UserService接口的实现类
    UserServiceImpl userServiceImpl = new UserServiceImpl();
  2. 获取对应的 ClassLoader
    ClassLoader classLoader = userServiceImpl.getClass().getClassLoader();
  3. 获取所有接口的Class:
    Class[] interfaces= userServiceImpl.getClass().getInterfaces();
  4. 创建一个将传给代理类的调用请求处理器
    InvocationHandler logHandler = new LogHandler(userServiceImpl);
  5. 根据上面提供的信息,创建代理对象
    UserService proxy = (UserService) Proxy.newProxyInstance(classLoader, interfaces, logHandler);

代理类UserServiceProxy特点如下:
1.UserServiceProxy 继承了 Proxy 类,并且实现了被代理的所有接口,以及equals、hashCode、toString等方法。
public final class UserServiceProxy extends Proxy implements UserService
2.类和所有方法都被 public final 修饰,所以代理类只可被使用,不可以再被继承。
在这里插入图片描述
3.每个方法都有一个 Method 对象来描述,Method 对象在static静态代码块中创建,以 m + 数字 的格式命名
在这里插入图片描述
4.调用方法的时候通过 super.h.invoke(this, m1, (Object[])null); 调用,其中的 super.h.invoke 实际上是在创建代理的时候传递给 Proxy.newProxyInstance 的 LogHandler 对象,它继承 InvocationHandler 类,负责实际的调用处理逻辑

cglib动态代理

两种代理的最主要的区别:
JDK动态代理基于Java反射机制实现,是基于接口的方式,换句话来说就是代理类和目标类都实现同一个接口,那么代理类和目标类的方法名就一样了。
CGLib动态代理基于ASM机制ASM Java 字节码操纵框架实现,是代理类去继承目标类,然后重写其中目标类的方法啊,这样也可以保证代理类拥有目标类的同名方法;

1.CGLib的基本使用
如jdk_proxy的LogInterceptor和CglibTest

描述动态代理的几种实现方式?分别说出相应的优缺点

代理可以分为 “静态代理” 和 “动态代理”,动态代理又分为 “JDK动态代理” 和 “CGLIB动态代理” 实现。

静态代理:代理对象和实际对象都继承了同一个接口,在代理对象中指向的是实际对象的实例,这样对外暴露的是代理对象而真正调用的是 Real Object

优点:可以很好的保护实际对象的业务逻辑对外暴露,从而提高安全性。
缺点:不同的接口要有不同的代理类实现,会很冗余

JDK 动态代理:

为了解决静态代理中,生成大量的代理类造成的冗余;

JDK 动态代理只需要实现 InvocationHandler 接口,重写 invoke 方法便可以完成代理的实现,

jdk的代理是利用反射生成代理类 Proxyxx.class 代理类字节码,并生成对象

jdk动态代理之所以只能代理接口是因为代理类本身已经extends了Proxy,而java是不允许多重继承的,但是允许实现多个接口

优点:解决了静态代理中冗余的代理实现类问题。

缺点:JDK 动态代理是基于接口设计实现的,如果没有接口,会抛异常。

CGLIB 代理:

由于 JDK 动态代理限制了只能基于接口设计,而对于没有接口的情况,JDK方式解决不了;

CGLib 采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑,来完成动态代理的实现。

实现方式实现 MethodInterceptor 接口,重写 intercept 方法,通过 Enhancer 类的回调方法来实现。

但是CGLib在创建代理对象时所花费的时间却比JDK多得多,所以对于单例的对象,因为无需频繁创建对象,用CGLib合适,反之,使用JDK方式要更为合适一些。

同时,由于CGLib由于是采用动态创建子类的方法,对于final方法,无法进行代理。

优点:没有接口也能实现动态代理,而且采用字节码增强技术,性能也不错。

缺点:技术实现相对难理解些。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值