一:qkcy
二 如何实现动态代理?JDK Proxy 和 CGLib 有什么区别?
- 动态代理的常用实现方式是反射。反射机制是指程序在运行期间可以访问、检测和修改其本身状态或行为的一种能力,使用反射我们可以调用任意一个类对象,以及类对象中包含的属性及方法。
- 动态代理除了反射的实现方式外,还可以通过 CGLib (ASM字节框架)来实现。
- JDK Proxy :代理的目标类需要实现接口,Java 语言自带,通过拦截器+反射实现。实现 InvocationHandler 接口,重写 invoke() 方
- CGLib:基于类的继承实现,是一个第三方库,需要额外的配置和依赖。
- 动态代理的常见使用场景有 RPC 框架的封装、AOP(面向切面编程)的实现、JDBC 的连接等。
三 hashMap相关
- 基于哈希表的数据结构实现,kay-value的键值对存值显示,可以通过健快速访问对应的值,
- 内部使用了数组和链表(红黑树)的形式存储元素,每个数组的bucket槽位对应一组数组或者树。
- 但要塞入一个键值对的时候,会根据hash算法计算key的hash值,得到具体下标后,回去找到对应下标的数组位置,因为hash算法可能会产生一样的下标,链表和红黑树就是为了解决这个冲突。
- 当链表的长度大于8,且数组大小大于等于64的时候,链表就会转化成红黑树。
获取元素时,先找到 hash 值对应的下标,再进一步判断 key 是否相同,从而找到对应的值
- JDK1.8对hashmap除了红黑树的变化外,还有以下改动
- ConcurrentHashMap1.7和1.8的区别
四 synchronize和lock相关
1 synchronize实现原理
依赖于JVM的Monitor(监视器锁)和对象头,当synchronize修饰在方法和代码块上的时候,会对特定的对象和类加锁,从而确保同一时刻只有一个线程能执行加锁的代码。
修饰方法:方法的常量池会增加一个 acc_synchronized 标识,当线程访问这个方式时候,会先去检查这个方法是否有这个标识,若有则需要获取监视器锁才能执行。
修饰代码块:会在修饰的代码块前后插入 monitorenter 和 monitorexit 指令,就相当于加解锁。
2 锁升级
偏向锁:
如果一个线程获得了锁,那么锁就进入偏向模式,当这个线程再次请求锁时, 无需再做任何同步操作,即获取锁的过程。(减少同一线程获取锁的代价。)
轻量级锁:
几个线程争夺锁,通过CAS争抢,然后剩下的自旋再重复cas
重量级锁:
线程竞争不使用自旋,将等待的线程进行阻塞
3 ReentrantLock 是lock的一个实现类,是基于AQS实现的一个可重入锁。
AQS 内部维护了一个同步标志位 state=0,初始值为0,线程加锁state+1,重入时候state会再次加一,所以这个值也可以表示获取锁的线程进行加锁的次数。
AQS还维护了一个队列,用于管理那些等待锁的线程。这个队列遵循FIFO原则,但也可以通过设置为公平锁来严格按照线程请求锁的顺序来排队。
4 synchronized和reentrantlock
相同点:都是作用于多线程同步到机制,保证一个时间点只能有一个线程合一执行某块代码业务,就等于说它们都是互斥锁。 另一个关键的相同点是都是可重入性的,意味着同一个线程可以多次获取同一个锁,不会死锁。
不同点:一个是java的关键字,一个是concurrent.locks包中的一个类
释放锁:synchronized是jvm自动释放(内部2个释放),lock必须手动调用unlock方法。
公平锁:synchronized是不公平的,不能保证等待时间最长的就能获取到,后者可以指定
五 多线程和并发相关
1 线程池参数和原理
主要工作原理如下:
1.默认情况下线程不会预创建,任务提交之后才会创建线程(不过设置 prestartAllCoreThreads 可以预创建核心线
程)。
2.当核心线程满了之后不会新建线程,而是把任务堆积到工作队列中。
3.如果工作队列放不下了,然后才会新增线程,直至达到最大线程数。
4.如果工作队列满了,然后也已经达到最大线程数了,这时候来任务会执行拒绝策略。
5.如果线程空闲时间超过空闲存活时间,并且线程线程数是大于核心线程数的则会销毁线程,直到线程数等于核心线程数(设置 allowCoreThreadTimeOut 为 true 可以回收核心线程,默