互联网java面试知识点整理

整理面试知识点
1. zookeeper 选举制度

https://www.cnblogs.com/shuaiandjun/p/9383655.html名词解释ServierId :服务器Id比如有三台服务器,编号分别是1,2,3。编号越大在选择算法中的权重越大。2、Zxid:数据ID服务器中存在的最大数据id值越大说明数据越新,在选举算法中数据越新权重越大。3、Epoch:逻辑时钟或者叫投票的次数,同一轮投票过程中的逻辑时钟值是相同的。每投完一次票这个数据就会增加,然后与接收到的其它服务器返回的投票信息中的数值相比,根据不同的值做出不同的判断。4、Server状态:选举状态
* LOOKING,竞选状态。
* FOLLOWING,随从状态,同步leader状态,参与投票。
* OBSERVING,观察状态,同步leader状态,不参与投票。
* LEADING,领导者状态。

在投票完成后,需要将投票信息发送给集群中的所有服务器,它包含如下内容。
* 服务器ID
* 数据ID
* 逻辑时钟
* 选举状

zookeper的选举机制,是在集群重启或者恢复阶段发起的。每一台机器都有自己的服务器ID 和Zxid:数据ID,每台机器会发起一次投票包括自己的服务id和Zxid:数据ID,逻辑时钟epoch状态Looking。每台机器也会与其他机器进行对比。(1)如果时钟epoch 比自己小 则表示目标机器在上一轮的投票中,则忽略结果继续发送自身的投票信息(2)如果时钟epoch比自己大,则更新自身的epoch 清空投票结果,比较自身和目标机器的zxid数据id。Zxid:数据ID相同时比较服务ID,如果发现目标机器比自身大的话证明数据比自己新,则会修改投票为目标机器的服务id和Zxid:数据ID(3)如果epoch和自己相同的话 则按照上面的规则比对自身的投票信息和目标机器的投票信息每次投票完成都会进行统计如果发现某台机器的票数超过半数则这台机器会被选举为Leader,其他机器变成follower 选举结束。集群只要保证有半数机器存活,集群就能正常运行,所以一般搭建集群时采用奇数台机器。脑裂问题如果遇到特殊情况比如第一台Leader由于网络原因被判定为不可用,这时也会发起选举,在选举出新的leader之后 旧的leader又恢复了正常此时集群中会有两台leader存在。每个leader生成之后都会产生一个新的时钟epoch,epoch的值是累加的。旧leader在向其他机器发出写请求时由于epoch值低于新leader,请求会被拒绝。旧leader在与新leader交互后会把自身变成follower。2.volatile应用原理
https://www.cnblogs.com/goodAndyxublog/p/11356402.html

volatile 具有synchronize的可见性 但是没有 它的原子性,也就是说valatile修饰的变量每次取值拿的都是缓存里的最新值。它可以作为一个内存栅栏防止被编译器进行指令重排列。
volatile的适用场景
一次性安全发布
在缺乏同步的情况下可能出现一个对象引用的新值(另一个线程写入的)和这个对象引用的旧值同时存在的情况下,这种就是导致双重检查锁定问题的根源(double-checked-lock)
这里拿单例模式里的双重验证锁来说明

这里对SingleTon instance对象用 volatile 修饰是为了防止该对象被编译器重排列。
如果不加的的话 instance = new SingleTon(); 这段代码的执行顺序会拆分成三步
1.
new 给instance分配对象内存(此操作会导致instance不为null )
2.
调用构造方法SingleTon(),执行初始化
3.
将对象引用 new SingleTon() 赋值给变量instance

其中1指令会首先被执行,但是2、3的指令的顺序可能会发生重排,因为单线程重排不会改变单线程内的执行结果,所以单线程是没有问题的,但是多线程会出现问题,比如一个线程先执行了第三部,那instance就会变成一个没有初始化的对象,另外一个线程就会取到这个对象后面的执行就会出现问题。加了volatile修饰之后这个对象的顺序就会保证是123的顺序, 多线程情况下如果执行到第二步被别的线程获取时取到的instance还是null会进到sychronized方法块里等待另一线程执行完成。

3.sychronized和lock的区别 sychronize的实现原理

1.sychronized是关键词层次的 jvm内的 lock是 类层面的
2.sychronized 锁是会自动释放的,lock的锁需要自己调用unlock()方法才能释放
3.sy无法判断锁的状态 lock可以获取锁的状态
4.sy的锁会有阻塞 如果线程A获取到锁,在A执行完成前线程B需要一直等待,而lock 则不一定需要等待,如果一直等待不到锁的释放线程会关闭。
5.sy适合少量代码的锁 lock可以对应大量代码的锁

sychronized的底层实现原理 通过javap工具来分析class的执行可以发现 代码块是根据 monitorenter 和 monitorexit 来保证代码锁的
任何对象都有一个monitor与之关联,当这个monitor被持有时,它处于锁定状态。当线程执行到 monitorenter处会去对象获取monitor的锁定状态,如果是锁定的则会等待释放。

4.redis 分布式锁 setNX
还可以通过redis 的timeout参数来控制key是否失效
加锁操作:jedis.set(key,random_value,“NX”,“EX”,timeOut)【保证加锁的原子操作】
key就是redis的key值作为锁的标识,value在这里作为客户端的标识,只有key-value都比配才有删除锁的权利【保证安全性】
通过timeOut设置过期时间保证不会出现死锁【避免死锁】
NX,EX什么意思?
NX:只有这个key不存才的时候才会进行操作,if not exists;
EX:设置key的过期时间为秒,具体时间由第5个参数决定
释放锁 用redis+lua来实现 保证释放锁的原子性
这里 要用lua先判断 获取的锁的值是不是自己设置的random_value保证自己只能释放自己的锁
然后再删除对应的redis锁值
if redis.call(‘get’,KEYS[1]) == ARGV[1] then
return redis.call(‘del’,KEYS[1])
else
return 0
end

5.jvm问题 包括垃圾回收 和调优
https://www.jianshu.com/p/23f8249886c6
垃圾回收:
JVM主要分为 新生代 老年代 和 持久代 3部分,其中新生代又分为一个伊甸园Eden 和两个 生存者空间 survivor
首先新对象的创建是在eden中 当 eden空间满了之后会触发第一次minor gc 会清除不可达的对象,存活的对象年龄+1 然后放入一个survivor中。当第二次 eden满了之后会触发第二次 minorgc eden中和survivor中存活的对象年龄+1,会全部存入另外一个survivor。
这里是新生代对象的移动,当一个对象经过多次gc后仍然存活的话会进入老年代。这个年龄是通过JVM的配置来控制的 --MaxTenuringThreshold 晋升年龄最大阈值 默认值是15
这里这个次数并不完全按照这个值来判断是否进入老年代。还有一个配置是 -XX:TargetSurvivorRatio 设定survivor区的目标使用率
如 如果Survivor空间中相同年龄所有对象大小的总和占survivor空间的比例达到了 目标使用率的配置时则会重新触发重新计算,取age 和 MaxTenuringThreshold 两者的最小值作为 晋升的阈值,在下次minorgc时达到年龄的会晋升到老年代

进入老年代的对象 如果老年代空间满了的话会进行major Gc

  • 在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。
  • 在老年代中,因为对象存活率高、没有额外空间对它进行分配担保,就必须使用标记-清理算法或者标记-整理算法来进行回收。

永久代Perm(jdk1.7的版本及以前的版本)

元数据区 Metaspace jdk1.8以后

6.mq 是pull和push的区别
kafka是pull模式 rockedmq支持pull和push
push模式的话 在mq将消息实时推送到消费者,实时性高,但是如果push的量过大过快的话会导致消费端出现问题
pull模式的话 是消费端自己控制拉取的速度,但是这样的问题就是pull的时间间隔不好控制。
7.java 并发 CAS 机制原理
CAS机制是 compareAndSet的缩写 是java并发的底层实现依据。是实现多线程同步的原子指令。将修改后的值与目标值进行比对如果相同的话证明没有其他线程同时修改,如果不同的话则表示其他线程已经修改过了,就放弃交换。
假设 i=0 ,设置k=i j= k+1 此时取到i的值如果是0的话就把j的值1 交换给j 否则则放弃交换操作,这样保证执行之后的数据是线程安全的CAS原理
1.
线程从内存中读取 i 的值,假如此时 i 的值为 0,我们把这个值称为 k 吧,即此时 k = 0。
2.
令 j = k + 1。
3.
用 k 的值与内存中i的值相比,如果相等,这意味着没有其他线程修改过 i 的值,我们就把 j(此时为1) 的值写入内存;如果不相等(意味着i的值被其他线程修改过),我们就不把j的值写入内存,而是重新跳回步骤 1,继续这三个操作。

public static void increment() {
do{
int k = i;
int j = k + 1;
}while (compareAndSet(i, k, j))
}
java中也提供了这种CAS的原子类 AtomicInteger
public class CASTest {
static AtomicInteger i = new AtomicInteger(0);
public static void increment() {
// 自增 1并返回之后的结果
i.incrementAndGet();
}
CAS会出现aba的问题,
就是a变成了b然后又被变回了a 此时比较 参数的值还是a但是实际上已经不是之前的a了,如果是引用类型的话就会出现问题。为了解决这个问题要引入一个版本号
的概念a1 b1 a2 b2 i=a1->b1->a2 此时参数i已经变成了a2 a2 != a1 ,所以修改不会被执行。
java里也体统了对应的类 AtomicStampedReference
java8对cas的优化
在线程量多的情况下冲突会比较严重,在执行第三步的时候由于i的值一直在被修改,所以会出现多个线程一直在运行但是值一直不变的情况。
java8里新增了一个cell[] 数组用来存放不同的线程,如果线程数量少的话还是按照正常的cas机制来执行,因为线程少冲突不明显
但是如果线程量比较大的情况下比如说100个线程,系统会把这100个线程分配到10个cell数组中来处理,每个cell【10】里面按照cas的机制来执行,
最后再把所有的cell数组的结果做汇总

8.反射机制好处,以及什么场景下用他
反射机制是 可以通过类的名字来实例化类,这种情况在框架中用的比较多,平常业务上比较少的使用,再就是在AOP上用的比较多
Class zclass =Class.forName(“className”);
Object ob = zclass.newInstance()
好处是提高了代码的拓展性,在开发的过程中可以不知道类的名字通过配置插件化的来获取
9.java AOP 具体应用及原理
spring里的AOP主要是应用了动态代理,包括JDK动态代理和cglib动态代理。主要是处理一些与业务无关但是却被业务模块共同调用的功能(例如日志处理、权限管理等)
JDK代理要求被代理的类必须实现一个接口,如果没有接口的话将采用cglib动态代理
定义切面和切点
切面类要用@ Aspect注解 和@component
切点的话 要用@pointCut(“包路径.类名.方法”) pointCut 可以用@Pointcut(" execution (包路径.类名.方法名(…))")可以用模糊*来做匹配

然后@Before 修饰的方法是前置方法会在切点之前触发
@After修饰的方法会在切点运行之后触发

https://www.cnblogs.com/LemonFive/p/10983875.html
10.hystrix限流&降级 熔断
1111

11.ThreadLocal 相关问题
https://www.jianshu.com/p/6fc3bba12f38
threadlocal解决多线程并发的一种解决方案,它是在线程内部的一个局部的变量,每个线程的变量数据都互不影响,避免了多线程操作公共变量的并发操作问题。
比sychronized使用更方便,更简单,使程序有更高的并发性。
主要有get 和set 两种方法
主要用在多线程共享同一个变量时为了避免其他线程对这个变量的修改可以把对象存入ThreadLocal中,这样每个线程拿到的对象都只是在该线程内部进行修改不会被其他线程获取到。
ThreadLocal 中 set 和 get 操作的都是对应线程的 table数组,因此在不同的线程中访问同一个 ThreadLocal 对象的 set 和 get 进行存取数据是不会相互干扰的。ThreadLocal特性
ThreadLocal和Synchronized都是为了解决多线程中相同变量的访问冲突问题,不同的点是
*
Synchronized是通过线程等待,牺牲时间来解决访问冲突
*
ThreadLocal是通过每个线程单独一份存储空间,牺牲空间来解决冲突,并且相比于Synchronized,ThreadLocal具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问到想要的值

例如 hibernate里的session的获取
private static final ThreadLocal threadLocal = new ThreadLocal();

//获取Session
public static Session getCurrentSession(){
Session session = threadLocal.get();
//判断Session是否为空,如果为空,将创建一个session,并设置到本地线程变量中
try {
if(session null&&!session.isOpen()){
if(sessionFactory
null){
rbuildSessionFactory();// 创建Hibernate的SessionFactory
}else{
session = sessionFactory.openSession();
}
}
threadLocal.set(session);
} catch (Exception e) {
// TODO: handle exception
}

return session;}

threadlocal使用不当会有内存泄露的风险 线程每次操作完后都需要执行一次remove操作
ThreadLocal的原理 每个线程内部都会存在着一个threadLocalMap 这个key是一个弱引用 就是threadlocal本身 value是对应的Object对象
因为key是一个弱引用 在gc以后可能会出现 threadLocal已经被回收 导致map出现一个key=null的 值 但是这个value值如果没有主动删除的话是不会被回收的就会导致内存泄露。
这里为什么要用弱引用,可以按以下两种情况来分析
1.假设key是强引用,引用的ThreadLocal对象被回收了,但是ThreadLocalMap还是持有ThreadLocal的强引用,如果没有手动删除还是不会被回收造成内存泄露
2.假设key是弱引用,引用的threadLocal被回收,由于theadLocalMap是持有threadLocal的弱引用,所以ThreadLocal还是会被回收,value值会在下次调用get set remove的时候被回收(这些方法会去主动清除key=null的值)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值