java-note-1

93 阅读13分钟

Interview

java

java-tips

  1. 复用代码的方式应该是 Has-A 关系而不是 Is-A 关系; 推荐思路....
  2. 使用Arrays.toString() 和 Arrays.deepToString() 方法来打印数组
  3. Immutable容器基本都是final的,一但创建不允许修改; 例如ImmutableList

assert

JVM禁用/启用断言:

  • 启用: -enableassertions 或者-ea
  • 禁用: -da 或者 -disableassertions

finalize

finalize()可以理解为GC之前的Bean执行的勾子函数;

Thread.start-run

启动一个线程是调用 start()方法,使线程所代表的虚拟处理机处于可运行状态,这意味着它可以由 JVM 调度并执行,这并不意味着线程就会立即运行。run()方法是线程启动后要进行回调(callback)的方法(理解为callback编程模式);

// callback编程模式
class Runnnable{
  void run();
}
class Thread{
  // has-a, 也可以是use-a
  Runnanble r;
  Thread(Runnable run){
    this.r = run
  }
  // callback-1: has-s
  public void start(){
    r.run() 
  }

  // callback-2: use-a
  public void call(Runnalble rr){
    rr.run() 
  }
}

通过反射创建对象

  1. String.class.newInstance() // Class
  2. String.class.getConstructor(String.class).newInstance(“Hello”); //构造函数

如何找到竞争条件

自己实现

  1. 生产者消费者: 同步队列
  2. 读写锁; 占有资源的读写线程分别的个数

Thread.interrupt

interrupt();使被阻塞的Thread对象抛出中断异常,从而中断阻塞;

  • interrupt 方法用于中断线程。调用该方法的线程的状态为将被置为”中断”状 态
  • interrupted 查询当前线程的中断状态,并且清除原状态; 如果一个线程被中断了,第一次调 用 interrupted 则返回 true,第二次和后面的就返回 false;
  • isInterrupted仅仅是查询当前线程的中断状态

ThreadLocal数据结构

WeadReference.map

检测一个线程是否拥有锁

在 java.lang.Thread 中有一个方法叫 holdsLock(),它返回 true

同步方法和同步块

同步块是更好的选择,因为它不会锁住整个对象(当然你也可以让它锁住整个对 象)。同步方法会锁住整个对象,哪怕这个类中有多个不相关联的同步块,这通常会导致他们停止执行并需要等待获得这个对象上的锁。 同步块更要符合开放调用的原则,只在需要锁住的代码块锁住相应的对象,这样 从侧面来说也可以避免死锁。

HashTable=对比=> Collections.synchronizedMap

  1. 1代同步:synchronized修饰方法实现: Vector,Hashtable
  2. 2代同步:synchronized(mutex)实现; Collections.synchronzed*
  3. 3代同步:分段锁,cow实现; CopyOnWriteList; ConcurrentHashMap

synchronized修饰方法 => synchronized(mutex) => Lock => CAS

jdk

String为什么使用final

final修饰class不能被继承;方法不能被修改;变量必须被初始化. => 方便使用JDK自己的String

String pool是Java管理基础类型的方法,防止频繁的创建销毁对象..JVM内部使用HashTable存储String pool过大会导致YGC, 在 Java 8 中,String Pool 依然还是在 Heap Space 中

  1. 基本的数据类型都是Class对象,例如Integer,主要为了多线层安全问题
  2. 防止继承重写; HashCode不允许修改,适合做Map的key
  3. 对于JVM字符串常量池的实现,字符串的引用使用。

Arraylist-Vector

ArrayList:线程不安全,每次扩容0.5, Vector: 线程安全,每次扩容1倍.

JVM

JVM内存结构内存模型、重排序、可见性、原子性、happens-before原则与解决之道

线程池

并发编程

JDK

Spring

内存泄漏-内存溢出

内存溢出: stack,heap,metaspace out of mem

内存泄漏: 通俗理解就是new的一段空间一直被某个实例所持有,但是却不在被使用; 内存泄漏有可能产生内存的溢出; 从GC的角度定义是:1- 这些对象是无用的,不会再被使用; 2- 对象是可达的,再引用的有相图中,不能被GC; 满足2个条件的对象就是内存memory leak 的对象

fastFail AND failSafe

快速失败机制,java集合的错误校验机制,抛出ConcurrentModificationException异常;监测到并发修改集合结构的时候触发fast-fail; (并发修改异常)

modCount记录集合修改的次数; 具体的源码参见List删除元素,总结出现的原因就是迭代器的remove和集合的remove函数实现不一样

支持的数据结构:ArrayList,HashMap

CopyonWrite(写时复制)

java为解决并发修改集合提供的工具类:java.util.concurrent;HashMap的替代类是ConcurrentHashMap,实现原理是通过分段锁;ArrayList的替代类是CopyOnWriterArrayList,实现方式是备份数据,保证最终的一直性;

一个通过锁解决;一个通过备份解决

linux

设计一个可靠的UDP协议

系统设计

抢红包

秒杀系统

缓存穿透-缓存击穿-缓存雪崩

缓存击穿: 击穿redis,命中数据库

  • 缓冲中key不存在/过期,会查询数据库,并发量大时数据库存在危险;
  • 数据没有热备,上线启动数据冲击数据库
  1. 做好数据预热(热备):一般使用在初次访问时加载缓存
  2. 缓存第一次访问的数据,避免重复访问数据库;

缓存穿透: 没有命中redis,也没有命中mysql

黑客使用缓存和数据库都不存在的key查询,可以用来攻击数据库

  1. 缓存无效的数据(key:null,并设置过期时间),这个方法要注意更新缓存
  2. BloomFilter,在缓存上增加BoolFilter,如果查询存在再走缓存逻辑;爬虫的URL过滤

缓存雪崩: redis失效

1:内存大面积失效或者集群崩溃,2:原来应该命中缓存的需求全部请求数据库,并对数据库造成压力。

  1. 集群:redis集群健壮,主从切换;内存策略:合适的内存淘汰测试,key过期删除机制
  2. 监控:做好缓存的监控
  3. redis做好持久化工作,便于数据恢复,宕机重启
  4. 关键业务的限流/缓存分级

热key: 单key的QPS超过redis上线

源码实战

1. 源码

zookeeper

2. 经验

从大堆文件选择最大的前10个

堆(top-K问题)

case

面试总共花费20天左右,包含4轮电话面试、1轮笔试、1轮主管视频面试、1轮hr视频面试

第一轮

电话面试(基础知识为主,约2小时): 1,先自我介绍,包含日常工作

2,基础知识

1)多线程(ThreadLocal(问了父子线程怎么共享数据 interitableThreadLocals) lock和synchronized区别(问HashMap1.7、1.8区别时带出)、 AQS原理(执行过程源码,入队出队的细节,源码细节)、 CountDownLatch和CyclicBarrier的区别是什么源码级别、 volatile从指令重排序,内存屏障,聊到总线风暴)所占比重较大 List区别

2)数据库(mysql索引(聚集索引、非聚集索引、索引结构(顺带会问各种树的特性)、 执行计划、 count1*区别、 举例优化sql、 MVCC和事务隔离级别的关系、 间隙锁、行锁(和多线程混合问的,乐观锁悲观锁等)、 唯一索引和普通索引的区别聊到了changeBuffer, 聊了页分裂合页合并)比重较大

3)jvm调优( 可达性分析算法中根节点有哪些、 cms和G1区别、 怎样GC调优、 怎样排查CPU彪高、内存彪高、逃逸分析)

4)redis数据结构、跳跃表、redis qps能上多少,怎么知道的、 sentinel和cluster区别和各自适用场景 redis cluster集群同步过程 redis单线程为什么快、 多大叫大key、热key产生原因和后果以及怎么解决、 本地缓存需要高时效性怎么办.....

5) spring的作用 spring 循环依赖怎么解决(说出三级缓存源码细节)、 spring aop原理(动态代理) spring bean生命周期(源码细节,以及各个位置的设计思路,有什么可扩展的)

6)dubbo服务暴露和引用过程,负载均衡策略,容错机制在哪里实现的源码

---> 分布式框架/分布式事务/分布式锁

7)项目中碰到的问题.........

8)为什么换工作,如果通过会直接说有笔试题,和你确认笔试时间。

第二轮

笔试两道题,第一题写代码,第二题写技术方案,以查询为主,考察锁粒度、时间粒度上的细节点。

第三轮

电话面试(解决方案为主,约1.5个小时) 1,自我介绍、项目介绍 2,说到缓存穿透,让我设计一个防止缓存穿透的解决方案,简单的就是存null值,但肯定会深究,可以结合布隆过滤器,设计分布式系统,里面又会问到流量分发到具体过滤器服务的方式,比如一致性hash算法,怎么调用?比如dubbo直连、等等细节会边说边问。 3,有没有做限流,设计一个侵入性最小的限流服务。 4,项目中碰到的问题,最好说框架本身问题,能提现个人能力,也避免问题太低级被面试官看low,刚好之前有发现一个dubbo的bug,所以这问题应该回答的还可以。 5,为什么换工作,每轮都会问,这个得想好。

第四轮

电话面试(项目为主,40分钟,应该是交叉面,问的不算多) 1,介绍最熟悉的项目,业务上有没有什么优化点;和同行业其他公司的差距和优势(估计是P7的标准问题吧。。。我是没咋说好)

2,dubbo服务调用过程(说着说着说到服务暴露和引用上面,他直接说这个之前问过了。。不用重复说,所以面试应该有记录面试问题) 3,NIO、BIO区别,NIO解决了什么问题,Netty线程模型(源码拷问)。 4,MQ相关(RocketMQ、kafaka奇怪的是你写啥面试官问啥,面试官啥都会,技术广度深度令人发指)

第五轮

电话面试(这位面试官比较较真,什么问题都会问具体数值,但和他挺聊得来的,向他请教阿里那边方案也会耐心指导,1.5小时) 1,项目介绍 2,听到说做了限流,限流标准(并发数?qps?并发数和qps关系?说出了5种限流方案和对应算法原理) 3,dubbo调用端怎么在jvm中生成对应服务?dubbo服务端和调用端超时时间设置和区别、dubbo长连接。 3,mysql行锁最大并发数?(秒杀项目指出) 4,设计秒杀系统,我说的异步的方式,会问怎么优化?改为了同步的方式,异步和同步区别?然后我也问了阿里那边 5,碰到哪些技术难点?怎么解决?有没有参考其他大厂?其他大厂方案什么样的?有没有关注阿里这边最新的技术? 6,刚刚的秒杀系统,会涉及到多个库表的更新,分布式事务怎么解决,我说的消息最终一致性,异步?有没有更好的方案?

同步TCC方式,TCC方式原理?(三个阶段的具体实现) 以上是技术面。

第六轮

主管视频面试:个人介绍、项目介绍为主,十五分钟结束。

第七轮

HR面试,项目介绍、职位介绍、离职原因、当前薪资,如果没什么问题,一天后会电话反馈待遇并确认是否接受。 不会问期望薪资是多少,后面会打电话告诉你评级是否接受,然后就开始安排体检了。 总结

面试整体难度中上,因为朋友对dubbo源码的研读比较深,所以基本上每一面都很加分,多线程环境都是常规问题,能回答道计算机底层就很加分了。 这次面试比较突然,他也没准备,全靠平时积累了,所以中间有些描述技术细节和项目的地方他觉得没回答好。

lagua

  1. 为什么想要这份工作 了解工作性质

  2. 面对过最大的挑战是什么 量化系统/公琏应用

  3. 见过最好和最坏的设计是什么 区块琏/netty

  4. 作为个人团队一员,如何达到最好的工作状态? 同理心 量化目标+阶段性成就 健康 一群人走的远,一个人走的快

  5. 面临过的最大挑战是什么?最蛋疼的bug是什么?解决过最有难度的问题是什么?

  6. 项目和工作中学到了什么?那些地方能做到更好?

introduce-yourself

redission-lock

锁升级->公平锁

问题总结

限流

  • 计数器(废弃): 存在限流边界陡增问题 => 时间片的边界出现2备的limit
  • 滑动窗口: 存在窗口内的请求陡增问题 => 时间片内一次给出了全部token,所以时间片内可以一次全部用完
  • 漏斗: 请求太匀速(水滴匀速流出)、不能提速 => 匀速进入,匀速流出 ; 不能请求提速
  • TokenBucket:=>匀速进入,但可以立即全部取出,只能一次取出全部剩余,剩余的token是按速度增加的而不是一次全给
request_uid_path(key) => tokenLimit=1000, TimeCost = 1000ms // 定义 qps=1000
tokenSpeed = tokenLimit / TimeCost;    // 每毫秒生成token的速度!!!
if(tb == null){
  tb = TokenBucket: tokenCount = 1,lastMills=currMills // init = 最近的访问时间
}else{
  tb = get_from_cache;
}
timeSpan = cur_request_mills - lastMills;  // timespan
timeSpanTokenGet = timeSpan * (tokenLimit / tokenSpan); // request间隔内生成的新的token
tokenRemain = min(timeSpanTokenGet + tb.tokenCount, tokenLimit); // 剩余的token
if(tokenRemain > 0){
  tokenRemain--;
  set_2_cache(tb:tokenCount = tokenRemain,lastMills = currMills_new);
}else{
  // reject_request
}

总结: 滑动窗口实现简单,TokenBucket是最优的;其他的不考虑

rocketmq相关的技术点

  • 消息过滤:通过tag订阅不同的消息,实现消息的分组(根据机器的进行分区) => 就能去除分布式锁去锁化
  • 事务消息: prepare -> commit -> callback
  • 异步刷盘 + mmap(0-copy)
  • tag过滤: broker比较tag.hashcode(), consumer端比较tag.equal()

dubbo

  • rpc -> microservice