项目介绍
大部分情况,这是一场面试的开门题,面试官问这个问题,主要是考察你的概述能力和全局视野。有的人经常抱怨自己每天在堆业务,但没有成长。事实上,很多情况下确实在堆业务,但并不是没有成长的。并非做中间件或者技术架构才是成长,例如我们的需求分析能力,沟通协作能力,产品思维能力,抽象建模能力等都是一个非常重要的硬实力。
好的,现在进入正文。
1、明确项目是做什么的
2、明确项目的价值。(为什么做这个项目,它解决了用户什么痛点,它带来什么价值?)
3、明确项目的功能。(这个项目涉及哪些功能?)
4、明确项目的技术。(这个项目用到哪些技术?)
5、明确个人在项目中的位置和作用。(你在这个项目的承担角色?)
6、明确项目的整体架构。
7、明确项目的优缺点,如果重新设计你会如何设计。
8、明确项目的亮点。(这个项目有什么亮点?)
9、明确技术成长。(你通过这个项目有哪些技术成长?)
Java基础
1、List 和 Set 的区别
2、HashSet 是如何保证不重复的
3、HashMap 是线程安全的吗,为什么不是线程安全的(最好画图说明多线程环境下不安全)?
HashMap 是线程不安全
上面说到,HashMap会进行resize操作,在resize操作的时候会造成线程不安全。下面将举两个可能出现线程不安全的地方。
1、put的时候导致的多线程数据不一致。
这个问题比较好想象,比如有两个线程A和B,首先A希望插入一个key-value对到HashMap中,首先计算记录所要落到的桶的索引坐标,然后获取到该桶里面的链表头结点,此时线程A的时间片用完了,而此时线程B被调度得以执行,和线程A一样执行,只不过线程B成功将记录插到了桶里面,假设线程A插入的记录计算出来的桶索引和线程B要插入的记录计算出来的桶索引是一样的,那么当线程B成功插入之后,线程A再次被调度运行时,它依然持有过期的链表头但是它对此一无所知,以至于它认为它应该这样做,如此一来就覆盖了线程B插入的记录,这样线程B插入的记录就凭空消失了,造成了数据不一致的行为。
2、另外一个比较明显的线程不安全的问题是HashMap的get操作可能因为resize而引起死循环(cpu100%),具体分析如下:
我们假设有两个线程同时需要执行resize操作,我们原来的桶数量为2,记录数为3,需要resize桶到4,原来的记录分别为:[3,A],[7,B],[5,C],在原来的map里面,我们发现这三个entry都落到了第二个桶里面。
假设线程thread1执行到了transfer方法的Entry next = e.next这一句,然后时间片用完了,此时的e = [3,A], next = [7,B]。线程thread2被调度执行并且顺利完成了resize操作,需要注意的是,此时的[7,B]的next为[3,A]。此时线程thread1重新被调度运行,此时的thread1持有的引用是已经被thread2 resize之后的结果。线程thread1首先将[3,A]迁移到新的数组上,然后再处理[7,B],而[7,B]被链接到了[3,A]的后面,处理完[7,B]之后,就需要处理[7,B]的next了啊,而通过thread2的resize之后,[7,B]的next变为了[3,A],此时,[3,A]和[7,B]形成了环形链表,在get的时候,如果get的key的桶索引和[3,A]和[7,B]一样,那么就会陷入死循环。
场景:resize()进行扩容时,需要rehash(),就是重新计算已有结点存放的位置。这个过程是非常耗费时间和空间的
问题关键:resize()方法中的transfer()方法进行位置重排时,因为不同的线程是对同一个数据块进行重排,所以才导致的问题
参考博客:https://blog.csdn.net/z69183787/article/details/64920074?locationNum=15&fps=1
4、HashMap 的扩容过程
当HashMap中的元素越来越多的时候,hash冲突的几率也就越来越高,因为数组的长度是固定的。所以为了提高查询的效率,就要对HashMap的数组进行扩容,数组扩容这个操作也会出现在ArrayList中,这是一个常用的操作,而在HashMap数组扩容之后,最消耗性能的点就出现了:原数组中的数据必须重新计算其在新数组中的位置,并放进去,这就是resize。
那么HashMap什么时候进行扩容呢?当HashMap中的元素个数超过数组大小*loadFactor时,就会进行数组扩容,loadFactor的默认值为0.75,这是一个折中的取值。也就是说,默认情况下,数组大小为16,那么当HashMap中元素个数超过16*0.75=12的时候,就把数组的大小扩展为 2*16=32,即扩大一倍,然后重新计算每个元素在数组中的位置,扩容是需要进行数组复制的,复制数组是非常消耗性能的操作,所以如果我们已经预知HashMap中元素的个数,那么预设元素的个数能够有效的提高HashMap的性能。扩容的时候需要reshash操作,其实就是讲所有的数据重新计算HashCode,然后赋给新的HashMap<K,V>,rehash的过程是非常耗费时间和空间的
5、HashMap 1.7 与 1.8 的 区别,说明 1.8 做了哪些优化,如何优化的?
https://blog.csdn.net/lovezhaohaimig/article/details/86595113
6、final finally finalize
1.简单区别:
final用于声明属性,方法和类,分别表示属性不可交变,方法不可覆盖,类不可继承。
finally是异常处理语句结构的一部分,表示总是执行。
finalize是Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,供垃圾收集时的其他资源回收,例如关闭文件等。
2.中等区别:
虽然这个单词在Java中都存在,但是并没太多关联:
final:java中的关键字,修饰符。
A).如果一个类被声明为final,就意味着它不能再派生出新的子类,不能作为父类被继承。因此,一个类不能同时被声明为abstract抽象类的和final的类。
B).如果将变量或者方法声明为final,可以保证它们在使用中不被改变.
1)被声明为final的变量必须在声明时给定初值,而在以后的引用中只能读取,不可修改。
2)被声明final的方法只能使用,不能重载。
finally:java的一种异常处理机制。
finally是对Java异常处理模型的最佳补充。finally结构使代码总会执行,而不管无异常发生。使用finally可以维护对象的内部状态,并可以清理非内存资源。特别是在关闭数据库连接这方面,如果程序员把数据库连接的close()方法放到finally中,就会大大降低程序出错的几率。
finalize:Java中的一个方法名。
Java技术使用finalize()方法在垃圾收集器将对象从内存中清除出去前,做必要的清理工作。这个方法是由垃圾收集器在确定这个对象没被引用时对这个对象调用的。它是在Object类中定义的,因此所的类都继承了它。子类覆盖finalize()方法以整理系统资源或者执行其他清理工作。finalize()方法是在垃圾收集器删除对象之前对这个对象调用的。
7、强引用 、软引用、 弱引用、虚引用
8、Java反射
9、Arrays.sort 实现原理和 Collection 实现原理
10、LinkedHashMap的应用
11、cloneable接口实现原理
12、异常分类以及处理机制
13、wait和sleep的区别
14、数组在内存中如何分配
Java 并发
1、synchronized 的实现原理以及锁优化?
2、volatile 的实现原理?
3、Java 的信号灯?
4、synchronized 在静态方法和普通方法的区别?
5、怎么实现所有线程在等待某个事件的发生才会去执行?
6、CAS?CAS 有什么缺陷,如何解决?
7、synchronized 和 lock 有什么区别?
8、Hashtable 是怎么加锁的 ?
9、HashMap 的并发问题?
10、ConcurrenHashMap 介绍?1.8 中为什么要用红黑树?
11、AQS
12、如何检测死锁?怎么预防死锁?
13、Java 内存模型?
14、如何保证多线程下 i++ 结果正确?
15、线程池的种类,区别和使用场景?
16、分析线程池的实现原理和线程的调度过程?
17、线程池如何调优,最大数目如何确认?
18、ThreadLocal原理,用的时候需要注意什么?
19、CountDownLatch 和 CyclicBarrier 的用法,以及相互之间的差别?
20、LockSupport工具
21、Condition接口及其实现原理
22、Fork/Join框架的理解
23、分段锁的原理,锁力度减小的思考
24、八种阻塞队列以及各个阻塞队列的特性
Spring
1、BeanFactory 和 FactoryBean?
2、Spring IOC 的理解,其初始化过程?
3、BeanFactory 和 ApplicationContext?
4、Spring Bean 的生命周期,如何被管理的?
5、Spring Bean 的加载过程是怎样的?
6、如果要你实现Spring AOP,请问怎么实现?
7、如果要你实现Spring IOC,你会注意哪些问题?
8、Spring 是如何管理事务的,事务管理机制?
9、Spring 的不同事务传播行为有哪些,干什么用的?
10、Spring 中用到了那些设计模式?
11、Spring MVC 的工作原理?
12、Spring 循环注入的原理?
13、Spring AOP的理解,各个术语,他们是怎么相互工作的?
14、Spring 如何保证 Controller 并发的安全?
Netty
1、BIO、NIO和AIO
2、Netty 的各大组件
3、Netty的线程模型
4、TCP 粘包/拆包的原因及解决方法
5、了解哪几种序列化协议?包括使用场景和如何去选择
6、Netty的零拷贝实现
7、Netty的高性能表现在哪些方面
分布式相关
1、Dubbo的底层实现原理和机制
2、描述一个服务从发布到被消费的详细过程
3、分布式系统怎么做服务治理
4、接口的幂等性的概念
5、消息中间件如何解决消息丢失问题
6、Dubbo的服务请求失败怎么处理
7、重连机制会不会造成错误
8、对分布式事务的理解
9、如何实现负载均衡,有哪些算法可以实现?
10、Zookeeper的用途,选举的原理是什么?
11、数据的垂直拆分水平拆分。
12、zookeeper原理和适用场景
13、zookeeper watch机制
14、redis/zk节点宕机如何处理
15、分布式集群下如何做到唯一序列号
16、如何做一个分布式锁
17、用过哪些MQ,怎么用的,和其他mq比较有什么优缺点,MQ的连接是线程安全的吗
18、MQ系统的数据如何保证不丢失
19、列举出你能想到的数据库分库分表策略;分库分表后,如何解决全表查询的问题
20、zookeeper的选举策略
21、全局ID
数据库
1、mysql分页有什么优化
2、悲观锁、乐观锁
3、组合索引,最左原则
4、mysql 的表锁、行锁
5、mysql 性能优化
6、mysql的索引分类:B+,hash;什么情况用什么索引
7、事务的特性和隔离级别
缓存
1、Redis用过哪些数据数据,以及Redis底层怎么实现
2、Redis缓存穿透,缓存雪崩
3、如何使用Redis来实现分布式锁
4、Redis的并发竞争问题如何解决
5、Redis持久化的几种方式,优缺点是什么,怎么实现的
6、Redis的缓存失效策略
7、Redis集群,高可用,原理
8、Redis缓存分片
9、Redis的数据淘汰策略
JVM
1、详细jvm内存模型
2、讲讲什么情况下回出现内存溢出,内存泄漏?
3、说说Java线程栈
4、JVM 年轻代到年老代的晋升过程的判断条件是什么呢?
5、JVM 出现 fullGC 很频繁,怎么去线上排查问题?
6、类加载为什么要使用双亲委派模式,有没有什么场景是打破了这个模式?
7、类的实例化顺序
8、JVM垃圾回收机制,何时触发MinorGC等操作
9、JVM 中一次完整的 GC 流程(从 ygc 到 fgc)是怎样的
10、各种回收器,各自优缺点,重点CMS、G1
11、各种回收算法
12、OOM错误,stackoverflow错误,permgen space错误
-
简短自我介绍
-
事务的ACID,其中把事务的隔离性详细解释一遍
-
脏读、幻影读、不可重复读
脏读
脏读意味着一个事务读取了另一个事务未提交的数据,而这个数据是有可能回滚的:
两个事务,事务A插入一条数据,但未提交,事务B在此期间进行了读取,读取到了事务A未提交的数据,造成脏读
幻影读
幻读,和不可重复读相似,也是同一个事务中多次读不一致的问题。但是不可重复读的不一致是因为它所要取的数据集被改变了,而幻读所要读的数据不一致却不是他所要读的数据改变,而是它的条件数据集改变。比如:Select id where name="ppgogo*",第一次读去了6个符合条件的id,第二次读时,由于事务B把第一个贴的名字由"dd"改成了“ppgogo9”,结果取出来7个数据,就好象发生了幻觉一样.
不可重复读(Unrepeatable Read)
不可重复读意味着,在数据库访问中,一个事务先后读取同一条记录,但两次读取的数据不同,我们称之为不可重复读。也就是说,这个事务在两次读取之间该数据被其它事务所修改。就是说事务A中两处读取数据,第一次读时是100,然后事务B把值改成了200,事务A再读一次,结果就发现值变了,造成A事务数据混乱。
-
红黑树、二叉树的算法
R-B Tree,全称是Red-Black Tree,又称为“红黑树”,它一种特殊的二叉查找树。红黑树的每个节点上都有存储位表示节点的颜色,可以是红(Red)或黑(Black)。
红黑树的特性:
(1)每个节点或者是黑色,或者是红色。
(2)根节点是黑色。
(3)每个叶子节点(NIL)是黑色。 [注意:这里叶子节点,是指为空(NIL或NULL)的叶子节点!]
(4)如果一个节点是红色的,则它的子节点必须是黑色的。
(5)从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。
-
平常用到哪些集合类?ArrayList和LinkedList区别?HashMap内部数据结构?ConcurrentHashMap分段锁?
-
jdk1.8中,对hashMap和concurrentHashMap做了哪些优化
concurrentHashMap 优化
-
链表改成了红黑树,当链表中的结点达到一个阀值TREEIFY_THRESHOLD时,会将链表转换为红黑树,查询效率提从原来的O(n),提高为O(logn)
- 抛弃了Segment分段锁机制,利用CAS+synchronized来保证并发更新的安全,底层依然采用数组+链表+红黑树的存储结构。
- basecount 记录元素数量,通过CAS更新
- countercells 记录元素变化个数,cas操作basecount失败时使用。
- 扩容的元素复制可并行进行。
-
如何解决hash冲突的,以及如果冲突了,怎么在hash表中找到目标值
hash值冲突是发生在put()时,当put的元素越来越多时,难免或出现不同的key产生相同的hash值问题,也即是hash冲突,当拿到一个hash值,通过indexFor(hash, table.length)获取数组下标,先查询是否存在该hash值,若不存在,则直接以Entry<V,V>的方式存放在数组中,若存在,则再对比key是否相同,若hash值和key都相同,则替换value,若hash值相同,key不相同,则形成一个单链表,将hash值相同,key不同的元素以Entry<V,V>的方式存放在链表中,这样就解决了hash冲突,这种方法叫做分离链表法,与之类似的方法还有一种叫做 开放定址法,开放定址法师采用线性探测(从相同hash值开始,继续寻找下一个可用的槽位)hashMap是数组,长度虽然可以扩大,但用线性探测法去查询槽位查不到时怎么办?因此hashMap采用了分离链表法。
从HashMap中get元素时,首先计算key的hashCode,找到数组中对应位置的某一元素,然后通过key的equals方法在对应位置的链表中找到需要的元素。
当hashMap没出现hash冲突时,没有形成单向链表,get方法能够直接定位到元素,但是,出现冲突后,形成了单向链表,bucket里存放的不再是一个entry对象,而是一个entry对象链,系统只能顺序的遍历每个entry直到找到想要搜索的entry为止,这时,问题就来了,如果恰好要搜索的entry位于该entry链的最末端,那循环必须要进行到最后一步才能找到元素,此时涉及到一个负载因子的概念,hashMap默认的负载因子为0.75,这是考虑到存储空间和查询时间上成本的一个折中值,增大负载因子,可以减少hash表(就是那个entry数组)所占用的内空间,但会增加查询数据的时间开销,而查询是最频繁的操作(put()和get()都用到查询);减小负载因子,会提高查询时间,但会增加hash表所占的内存空间。
-
synchronized 和 ReentranLock的区别?
-
ThreadLocal?应用场景?
-
Java GC机制?GC Roots有哪些?
-
MySQL行锁是否会有死锁的情况?
02
拼多多技术二面
-
乐观锁和悲观锁了解吗?JDK中涉及到乐观锁和悲观锁的内容?
-
Nginx负载均衡策略?
-
Nginx和其他负载均衡框架对比过吗?
-
Redis是单线程?
-
Redis高并发快的原因?
-
如何利用Redis处理热点数据
-
谈谈Redis哨兵、复制、集群
-
工作中技术优化过哪些?JVM、MySQL、代码等都谈谈
03
拼多多技术三面
-
Spring Cloud用到什么东西?如何实现负载均衡?服务挂了注册中心怎么判断?
-
网络编程nio和netty相关,netty的线程模型,零拷贝实现
-
分布式锁的实现你知道的有哪些?具体详细谈一种实现方式
-
高并发的应用场景,技术需要涉及到哪些?怎样来架构设计?
-
接着高并发的问题,谈到了秒杀等的技术应用:kafka、redis、mycat等
-
最后谈谈你参与过的项目,技术含量比较高的,相关的架构设计以及你负责哪些核心编码