1. 上来就问了简历上的三个项目,挨个问的,问的很细,在问的过程中顺带提了一些八股。
2. 项目中分布式锁为什么了使用了redission(只说了对它比较熟悉);
3. Java中有哪些锁(回答了synchronize和lock,简单介绍了一下两种锁的优缺点),追问synchronize原理(不会);
1.synchronized
是依赖于对象实现的锁功能(对象头以及Monitor
)
monitor
对象存在于堆空间内,每个Java
对象的对象头,其中markword
存放指向Monitor
对象的指针,synchronized
关键字便是通过这种方式获取锁的
2.锁升级
无锁状态:
偏向锁:
如果一个线程获得了锁,那么锁就进入偏向模式,此时Mark Word
的结构也变为偏向锁结构,当这个线程再次请求锁时,无需再做任何同步操作,即不需要再走获取锁的流程,这样就省去了大量有关锁申请的操作,从而也就提高程序的性能。
轻量级锁:之前持有偏向锁的线程,会优先进行cas
,尝试设置mrakword
中的锁信息指针。
如果成功,则说明获取到轻量级锁;如果失败,则说明锁已经被其他持有了,此时记录线程的重入次数(把lock record
的markword
设置为null
),此时线程会自旋(自适应自旋),确保在竞争不激烈的情况下,仍然可以不膨胀为真正意义上的“内核态重量级锁”,从而减少消耗。
如果自旋后还未等到锁,则说明目前竞争较重,需要膨胀为重量级的锁
重量级锁:重量级锁就是传统意义的互斥锁了,当出现较大竞争、锁膨胀为重量级锁时,对象头的markword
指向堆中的monitor
总结:
- 无锁态:
JVM
启动后四秒内的普通对象,和四秒后的匿名偏向锁对象 - 偏向锁状态:只有一个线程进入临界区
- 轻量级锁状态:多个线程交替进入临界区
- 重量级锁:多个线程同时进入临界区
3.锁优化:
1.同步消除
在动态编译同步块的时候,JIT编译器可以借助逃逸分析来判断同步块所使用的锁对象是否只能够被一个线程访问而没有被发布到其他线程。
如果同步块所使用的锁对象通过这种分析被证实只能够被一个线程访问,那么JIT编译器在编译这个同步块的时候就会取消对这部分代码的同步。这个取消同步的过程就叫同步省略,也叫锁消除。
public void f() {
Object hollis = new Object();
synchronized(hollis) {
System.out.println(hollis);
}
}
优化后
public void f() {
Object hollis = new Object();
System.out.println(hollis);
}
2.锁重入
3.线程等待与唤醒机制
notify/notifyAll
和wait
方法,在使用这三个方法时,必须处于synchronized
代码块或者synchronized
方法中,否则就会抛出IllegalMonitorStateException
异常。
4. 用Mysql做秒杀,数据库出现幻读怎么解决(回答了Mysql默认事务隔离级别是可重复读,已经解决幻读问题了)
5. AOP的实现原理(动态代理)
代理模式:一个客户不想或者不能够直接引用一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用
代理模式的优点
- 优点一:可以隐藏真实目标类的实现;
- 优点二:可以实现客户与真实目标类间的解耦,在不修改真实目标类代码的情况下能够做一些额外的处理。
动态代理也分为两类:基于接口的代理和基于继承的代理;两类实现的代表分别是:JDK代理与CGlib代理。
jdk代理:
- 通过java.lang.reflect.Proxy类来动态生成代理类;
- 代理类要实现InvocationHandler接口;
- JDK代理只能基于接口进行动态代理;
因为利用JdkProxySubject生成的代理类实现了接口,所以目标类中所有的方法在代理类中都有。现在,如果我们在subject接口中新增加了一个goodBye()方法,然后再RealSubject中对goodBye()方法进行实现,但是在代理类中,我们不需要再去为goodBye()方法再去写一个代理方法,而是通过反射调用目标对象的方法,来动态的生成代理类。
代理的本质其实就是一种对行为的监听,对代理对象($proxy InvocationHandler)的一种监听行为。
CGlib代理:
为一个类创建子类,并在子类中采用方法拦截的技术拦截所有对父类方法的调用,并顺势加入横切逻辑。CGlib是针对类来实现代理的,原理是对指定的业务类生成一个子类,并覆盖其中业务方法实现代理,因为采用的是继承,所以不能对final修饰的类进行代理。CGlib和JDK的原理类似,也是通过方法去反射调用目标对象的方法。
Spring如何选择用JDK还是CGLib
- 当Bean实现接口时,Spring就会用JDK的动态代理;
- 当Bean没有实现接口时,Spring使用CGlib的代理实现;
- 可以通过修改配置文件强制使用CGlib;