知识体系梳理

[1] Java 线程的状态
300

[2] 进程和线程的区别,进程之间如何通讯,线程之间如何通讯?

进程有自己的地址空间和运行环境,他们之间的通讯其实可以理解成应用之间的通讯,可以通过管道、网络等来通讯。线程拥有更少的资源,一个进程可以有多个线程,线程之间的通讯则可以通过锁、信号量等来实现同步,可以通过共享内存的方式来实现数据的通讯。

[3]HashMap的实现原理是什么?和HashTable、ConcurrentHashMap有什么区别

以下的回答是基于JDK 1.8来说的。

HashMap 里有几个关键的概念,

  • capacity,默认是16,也可以手动指定,但最后会被计算成2的指数倍。
  • loadFactor,默认是0.75
  • threadShold,阈值(capacity*loadFactor),超过这个阈值就会做resize,resize会double Map的capacity.
    HashMap里是用一个 Node[] 数组来存储数据的,Node 有以下几个关键的属性:hash, key, value, next.
    put 操作的时候,根据对象的hash值找到数组下标,没有被占就直接放进去,如果有值了,就通过 next 串起来。当链表的大小超过TREEIFY_THRESHOLD的时候,会对这个链表进行树化操作,将Node 转换成 TreeNode(多了prev, left, right等属性)

HashTable 和 HashMap 最大的区别,

  • HashTable 是线程安全的,实现上就是在方法上加了 synchronized
  • HashTable key 不予许为 null,null 的话会抛异常
  • 实现上比较简单,没有树化的操作

ConcurrentHashMap 和 HashTable 一样是线程安全的,但 HashTable锁的粒度比较大,ConcurrentHashMap 采用的Segment,锁里的粒度是在Segment上,性能更好,

[4]索引
下面的讨论不考虑考虑全文索引(MyISAM),Hash索引,只针对B树索引,索引的实现原理,B+树:
400
了解了索引的实现原理后,在创建索引的时候,需要关注这么几个事项:

  • 区分度
  • 最左匹配,并不是说在写SQL的时候要按这个顺序写,而是在建索引的时候要考虑这个顺序

聚簇索引和非聚簇索引,以及他们各自的优缺点。
400
3A索引,1. 索引全部都走到; 2. 数据全在索引上,不需要二次索引; 3. 排序字段刚好也是索引

[5]事务的隔离级别
如下,但这是SQL92的标准,具体数据库的实现和这个是有出入的。能够举例说明脏读、不可重复读、幻象读等各类情况是什么意思。

隔离级别 脏读 不可重复读 幻象读 第一类更新丢失 第二类更新丢失
READ UNCOMMITED 不会
READ COMMITED 不会 不会
REPEATABLE READ 不会 不会 不会 不会
SERIALIZABLE 不会 不会 不会 不会 不会

[6]Spring事务的传播级别
传播级别比较多,重点关注以下三个。能够举例(代码)说明这三种传播级别都是什么意思。

类型 说明
PROPAGATION_REQUIRED 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是 最常见的选择。
PROPAGATION_REQUIRES_NEW 新建事务,如果当前存在事务,把当前事务挂起
PROPAGATION_NESTED 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与 PROPAGATION_REQUIRED 类似的操作

[7]一致性Hash及它的使用场景
一致性哈希算法及其在分布式系统中的应用
考虑容错性、可扩展性、均衡性
一般用于做负载均衡,比如Motan中的cluster模块

[8]为什么 equals 和 hashcode要一起重写
[9]Object类有哪些方法
equals/hashcode/clone/toString/wait/notify

[10]JVM内存模型
400
[11]JVM运行时数据区
400
[12]GC算法
评价一个GC算法有以下几个标准:

  • 吞吐量,单位时间的处理能力
  • 最大暂停时间(Stop The World)
  • 堆使用效率
  • 访问的局部性
    首先来看下一些经典的GC算法以及他们各自的优缺点,然后再来看JVM中的GC. 下面说的都是最基础的算法,实际的比这复杂很多很多。
  • 标记-清除
    主要有标记、清除、合并、分配几个操作,优点是简单,缺点是碎片化,分配速度慢
  • 引用计数
    即刻回收,最大暂停时间短,缺点就是计数复杂,计数器占用空间
  • 复制算法
    分两块内存,每次GC把活得的对象copy到另一块,之前的一块直接全部干掉。
    优点,吞吐量高(这里要思考为什么吞吐量高),没有碎片化,分配速度也快,缺点就是堆使用率低

    上面的GC都有各自的优缺点,分代GC就是综合几种GC算法的一种GC方式。
    500
    青年代使用复制算法,老年代使用标记算法,但这里存在一个问题,可能会存在跨代引用(挺多人踩过这个坑,可以关注下)的问题。

    JVM里这么几种垃圾收集器
    500

[13]标什么情况下回出现Full GC,什么情况下会出现Young GC
Young GC

  1. Eden发生GC的时候

Full GC

  1. System.GC()
  2. 年轻代晋升到老年代,要在老年代分配空间的时候,发现空间不够时
  3. 分配担保

[14]线程池

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Executor
ExecutorService
Executors
Runnable

newFixedThreadPool
newSingleThreadExecutor
newCachedThreadPool

public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
https://github.com/fdx321/gitbook_code/blob/master/jdk_1_8/concurrent/threadpoolexecutorjava.md

[15]Runnable/Callable/Future/FutureTask

[17]如何写代码让程序出现各种OOM或StackoverFlow
首先你得知道JVM的内存布局,以及这些内存都是干什么用的。
HeapOutOfMemory
Young OutOfMemory,年轻代溢出,MaxTenuringThreshold设置的很大,让年轻代的对象永远无法晋升到老年代
MethodArea OutOfMemory, 用ClassLoader加载很多类
ConstantPool OutOfMemory
DirectMemory OutOfMemory
Stack OutOfMemory Stack OverFlow,一直递归调用

[18]长轮询
为什么要使用长轮询(Push, Pull 分析各自的优缺点)、怎么实现

[19]几个常用的JDK命令
jps, jstack, jmap, jconsole, jinfo, jhat, javap, …

[20]长连接为什么需要发心跳包?

[21]ClassLoader
500
双亲委派,Tomcat为啥打破双亲委派

[22]HTTP协议
200 404 401 400 405 500 502
[23]TCP协议
三次握手、四次挥手、Time_Await、拥塞控制、超时重试、保活
[24]数据库连接池实现原理
看过MyBatis自带连接池、DBCP以及Druid的源码,可以总结一下他们的共同点和区别。

Driver注册,然后通过 DriverManager getConnection. DataSource 主要的接口方法就是 getConnection.

MyBatis 数据库连接池主要就是维护了两个Map(Active 和 Idle 的连接),Map 里的连接是代理后的连接,这里用的是JDK的动态代理,PooledConnection 实现了 InvocationHandler,invoke 方法里会去做判断,如果是close就将真实的连接放回Map里,如果是其他方法,就直接透传。

DBCP 数据库连接池,池的功能是通过commons-pool来实现的,对 JDBC 的 Connection/Statement等做了静态代理

Druid,也是做了连接池,做了各种代理,但和DBCP最大的不同就是多了一层SQL Parser, 可以对SQL做各种审计、监控、filter等

[25]Netty线程池是怎么用的
BIO/NIO/NIO2(AIO)
同步阻塞/伪异步/Reactor单线程/Reactor多线程/Reactor主从多线程

[26]AOP实现原理

[27]Spring的启动过程
web.xml里配置了ContextLoaderListener, Tomcat在启动或部署项目的过程中,会去调用该监听器,然后会去call ContextLoader#initWebApplicationContext,然后实例化 XmlWebApplicationContext,这个就是Spring的IOC容器了,然后就是去解析XML配置文件,注册BeanDefinition.

[28]自己开发框架如何与Spring集成
看过MyBatis和Motan的源码,可以看看他们是如何与Spring集成的。
MyBatis:
实现FactoryBean接口,getObject方法。比如SqlSessionFactoryBean/MapperFactoryBean

Motan:
与 Spring 集成的切入点在于,通过 motan-core 中 spring.schemas、spring.handlers 这两个配置文件指定自定义的 schema 和对应的 NamespaceHandler。NamespaceHandler 中会指定每个自定义标签的BeanDefinitionParser 和对应的 Class.

[29]SPI扩展点

[30]Tomcat
[31]RPC
[32]MQ
[33]介绍几种常用的设计模式以及他们的使用场景

1. 责任链模式:
Tomcat:
Servlet Filter,Filter 接口定义了 doFilter(request,response,filterChain)方法,
FilterChain 接口定义了 doFilter(request,response)方法,Tomcat的ApplicationFilterChain实现了该接口,负责保存filter,然后按序执行。

Pipeline, Valve. StandardPipeline

2. 代理模式:
静态代理:
dhcp、druid等数据库连接池实现,都代理了JDBC Connection、Statement
动态代理:
RPC 框架

3. 工厂模式:
MyBatis:
SqlSessionFactory、MapperProxyFactory

4. 观察者模式(发布订阅模式)
Tomcat:
LifecycleBase、LifecycleListener、LifecycleSupport

5. 单例模式

[34]分布式事务,两阶段提交
知道两阶段提交(画图说明),以及两阶段提交中会遇到的问题(Timeout unkonwn问题,回查),怎么解决。

[35]如何实现分布式锁

[36]如何实现分布式Session

  • Tomcat Session单机和Cluster实现
  • Session 复制
  • Session Sticky
  • Session 集中管理

[37]聊聊负载均衡

从大的架构层去分析,最外层是F5/LVS –> Ngnix –> Web接入层 –(RPC实现负载均衡)-> 后台服务 —-> DB

[38]聊聊缓存的使用
缓存使用总结
关注以下几个问题:

  • 更新缓存还是淘汰缓存
  • 淘汰缓存和更新数据库的先后顺序以及各自会有什么问题
  • 缓存穿透问题
  • 热点key问题(一旦key失效。。)
  • 过载问题(缓存预热)

[39]CDN实现原理
CDN的原理以及其中的一些技术
[40]秒杀系统设计
[41]超卖问题
[42]Fork Join
[43]JVM的一些关键参数是什么意思
-Xmx -Xms -Xmn -Xss
-XX:NewRatio -XX:SurvivorRatio
-XX:NewSize, -XX:MaxNewSize -XX:MaxTenuringThreshold
[44]动态代理
[45]ThreadLocal
[46]BIO,NIO,AIO
[47]Sleep和Wait
[48]wait和notify
Java多线程之wait(),notify(),notifyAll()
[49]volatile
[50]BeanFactory和FactoryBean以及它们的使用场景
[51]BloomFilter
BloomFilter——大规模数据处理利器