技术专家总结分析

1.非技术问题

A、 整理一个项目中遇到的哪些棘手的问题,这些问题带来了什么问题,当时想了哪些解决方案,最终为何选择了这个方案,这个方案的结果是什么,后续应该如何在系统设计时提前考虑这些问题;

B、 当XXX官问你有什么想问的问题时。要注意不要问xinchou相关信息。主要问团队情况、过来做什么事情,可能会遇到的挑战

C、 开放性问题注意的点,开放性问题没有标准答案,核心就是不要把话说太死,以自己的了解说个大概即可。多方面考虑开放性问题。

D、 XXX注意不要过于表现自己,根据XXX官的问题,回答核心问题即可,有时隐藏部分实力,也是极好。

E、 XXX注意,当一个问题,你觉得没有准备好的时候,可以重复XXX官的问题,放慢语速。然后在这段时间去思考这个问题。

F、 回答问题要有条理性、逻辑性。要回答一个问题时,先在脑袋里想一想,然后再组织一下,最后再说出来。这样会更具有结构化的东西出来。

2. 常见技术

一些实现原理性的东西。

1、java如何垃圾回收

2、java如何判断内存为垃圾

3、java out of memory有哪几种情况出现

4、分布式系统雪崩产生原因,如何防止雪崩

5、tcp拥塞算法

6、微服务和soa的区别,微服务的技术难点

7、谈谈redis的使用,redis的数据主从同步

Redis集群高可用,主从同步,哨兵

--------------------------------------

ConcurrentHashMap 1.7和1.8的区别

1、整体结构

1.7:Segment + HashEntry + Unsafe

1.8: 移除Segment,使锁的粒度更小,Synchronized + CAS + Node + Unsafe

2、put()

1.7:先定位Segment,再定位桶,put全程加锁,没有获取锁的线程提前找桶的位置,并最多自旋64次获取锁,超过则挂起。

1.8:由于移除了Segment,类似HashMap,可以直接定位到桶,拿到first节点后进行判断,1、为空则CAS插入;2、为-1则说明在扩容,则跟着一起扩容;3、else则加锁put(类似1.7)

3、get()

基本类似,由于value声明为volatile,保证了修改的可见性,因此不需要加锁。

4、resize()

1.7:跟HashMap步骤一样,只不过是搬到单线程中执行,避免了HashMap在1.7中扩容时死循环的问题,保证线程安全。

1.8:支持并发扩容,HashMap扩容在1.8中由头插改为尾插(为了避免死循环问题),ConcurrentHashmap也是,迁移也是从尾部开始,扩容前在桶的头部放置一个hash值为-1的节点,这样别的线程访问时就能判断是否该桶已经被其他线程处理过了。

5、size()

1.7:很经典的思路:计算两次,如果不变则返回计算结果,若不一致,则锁住所有的Segment求和。

1.8:用baseCount来存储当前的节点个数,这就设计到baseCount并发环境下修改的问题(说实话我没看懂-_-!)

----

(1)JDK1.7用的是头插法,而JDK1.8及之后使用的都是尾插法,那么他们为什么要这样做呢?因为JDK1.7是用单链表进行的纵向延伸,当采用头插法时会容易出现逆序且环形链表死循环问题。但是在JDK1.8之后是因为加入了红黑树使用尾插法,能够避免出现逆序且链表死循环的问题。

(2)扩容后数据存储位置的计算方式也不一样:1. 在JDK1.7的时候是直接用hash值和需要扩容的二进制数进行&(这里就是为什么扩容的时候为啥一定必须是2的多少次幂的原因所在,因为如果只有2的n次幂的情况时最后一位二进制数才一定是1,这样能最大程度减少hash碰撞)(hash值 & length-1)

2、而在JDK1.8的时候直接用了JDK1.7的时候计算的规律,也就是扩容前的原始位置+扩容的大小值=JDK1.8的计算方式,而不再是JDK1.7的那种异或的方法。但是这种方式就相当于只需要判断Hash值的新增参与运算的位是0还是1就直接迅速计算出了扩容后的储存方式。

(3)JDK1.7的时候使用的是数组+ 单链表的数据结构。但是在JDK1.8及之后时,使用的是数组+链表+红黑树的数据结构(当链表的深度达到8的时候,也就是默认阈值,就会自动扩容把链表转成红黑树的数据结构来把时间复杂度从O(n)变成O(logN)提高了效率)

----------------------------

redis据你所知是单线程的,为什么redis还可以快?

开发语言

现在我们都用高级语言来编程,比如 Java、python 等。也许你会觉得 C 语言很古老,但是它真的很有用,毕竟 unix 系统就是用 C 实现的,所以 C 语言是非常贴近操作系统的语言。Redis 就是用 C 语言开发的,所以执行会比较快。

纯内存访问

Redis 将所有数据放在内存中,非数据同步正常工作时,是不需要从磁盘读取数据的

单线程

第一,单线程简化算法的实现,并发的数据结构实现不但困难而且测试也麻烦。

第二,单线程避免了线程切换以及加锁释放锁带来的消耗,对于服务端开发来说,锁和线程切换通常是性能杀手。

非阻塞多路 I/O 复用机制

I/O 多路复用实际上是指多个连接的管理可以在同一进程。多路是指网络连接,复用只是同一个线程。在网络服务中,I/O 多路复用起的作用是一次性把多个连接的事件通知业务代码处理,处理的方式由业务代码来决定,该方法就会返回可读 / 写的 FD 个数。

Redis 使用 epoll 作为 I/O 多路复用技术的实现,再加上 Redis 自身的事件处理模型将 epoll 的 read、write、close 等都转换成事件,不在网络 I/O 上浪费过多的时间,从而实现对多个 FD 读写的监控,提高性能。

为什么用skiplist  不用平衡树:

1. 内存,内存占用skiplist 较为灵活一些。每个节点平均1.3.平衡树是2

2. 范围查找,平衡树范围查找,找到范围小值后,还需要中序遍历,查到大值节点。skiplist直接底层level往后查询就可以了。

3. 新增删除,平衡树会导致子数的调整,而skiplist只需要修改相邻节点的指针。

4. 从算法实现难度上来比较,skiplist比平衡树要简单得多

set命令要用 setkey value px milliseconds nx;保证原子性

value要具有唯一性,释放锁时要验证value值,不能误解锁;

解锁要使用lua脚本,也是为了保证原子性

Redis官方也指出该方法有安全隐患就是在主从复制模式下会导致两个线程可能会同时持有一个锁,如果业务允许如此,则推荐使用这种方案,毕竟实现简单,易维护。

如果对锁的要求非常高的场景,Redis官方建议使用RedLock算法。

高并发场景下的问题

以下问题不是说在并发不高的场景下不容易出现,只是在高并发场景下出现的概率更高些而已。

性能问题。 性能问题来自于两个方面。

获取锁的时间上。如果redlock运用在高并发的场景下,存在N个master节点,一个一个去请求,耗时会比较长,从而影响性能。这个好解决。通过上面描述不难发现,从多个节点获取锁的操作并不是一个同步操作,可以是异步操作,这样可以多个节点同时获取。即使是并行处理的,还是得预估好获取锁的时间,保证锁的TTL > 获取锁的时间+任务处理时间。

被加锁的资源太大。加锁的方案本身就是会为了正确性而牺牲并发的,牺牲和资源大小成正比。这个时候可以考虑对资源做拆分,拆分的方式有两种:

从业务上将锁住的资源拆分成多段,每段分开加锁。比如,我要对一个商户做若干个操作,操作前要锁住这个商户,这时我可以将若干个操作拆成多个独立的步骤分开加锁,提高并发。

用分桶的思想,将一个资源拆分成多个桶,一个加锁失败立即尝试下一个。比如批量任务处理的场景,要处理200w个商户的任务,为了提高处理速度,用多个线程,每个线程取100个商户处理,就得给这100个商户加锁,如果不加处理,很难保证同一时刻两个线程加锁的商户没有重叠,这时可以按一个维度,比如某个标签,对商户进行分桶,然后一个任务处理一个分桶,处理完这个分桶再处理下一个分桶,减少竞争。

重试的问题。 无论是简单实现还是redlock实现,都会有重试的逻辑。如果直接按上面的算法实现,是会存在多个client几乎在同一时刻获取同一个锁,然后每个client都锁住了部分节点,但是没有一个client获取大多数节点的情况。解决的方案也很常见,在重试的时候让多个节点错开,错开的方式就是在重试时间中加一个随机时间。这样并不能根治这个问题,但是可以有效缓解问题,亲试有效。

节点宕机

对于单master节点且没有做持久化的场景,宕机就挂了,这个就必须在实现上支持重复操作,自己做好幂等。

对于多master的场景,比如redlock,我们来看这样一个场景:

假设有5个redis的节点:A、B、C、D、E,没有做持久化。

client1从A、B、C 3个节点获取锁成功,那么client1获取锁成功。

节点C挂了。

client2从C、D、E获取锁成功,client2也获取锁成功,那么在同一时刻client1和client2同时获取锁,redlock被玩坏了。

怎么解决呢?最容易想到的方案是打开持久化。持久化可以做到持久化每一条redis命令,但这对性能影响会很大,一般不会采用,如果不采用这种方式,在节点挂的时候肯定会损失小部分的数据,可能我们的锁就在其中。

另一个方案是延迟启动。就是一个节点挂了修复后,不立即加入,而是等待一段时间再加入,等待时间要大于宕机那一刻所有锁的最大TTL。

但这个方案依然不能解决问题,如果在上述步骤3中B和C都挂了呢,那么只剩A、D、E三个节点,从D和E获取锁成功就可以了,还是会出问题。那么只能增加master节点的总量,缓解这个问题了。增加master节点会提高稳定性,但是也增加了成本,需要在两者之间权衡。

任务执行时间超过锁的TTL

之前产线上出现过因为网络延迟导致任务的执行时间远超预期,锁过期,被多个线程执行的情况。

这个问题是所有分布式锁都要面临的问题,包括基于zookeeper和DB实现的分布式锁,这是锁过期了和client不知道锁过期了之间的矛盾。

在加锁的时候,我们一般都会给一个锁的TTL,这是为了防止加锁后client宕机,锁无法被释放的问题。但是所有这种姿势的用法都会面临同一个问题,就是没发保证client的执行时间一定小于锁的TTL。虽然大多数程序员都会乐观的认为这种情况不可能发生,我也曾经这么认为,直到被现实一次又一次的打脸。

事件驱动,所谓的reactor模式

redis问题总结:

1;FD泄露:

当系统剩余内存不足以fork子进程时,AOF重写子进程启动失败,Redis不断尝试启动AOF重写子进程,终于在数小时后将允许的fd数耗尽,此时错误log为“Error opening /setting AOF rewrite IPC pipes: Numerical result out of range”。

解决方案:

1、设置linux系统的vm.overcommit_memory = 1(其意义可查阅man手册,本文不再展开),尽量减少子进程fork失败的情况;

2、设置Redis的maxmemory限制,当内存使用达到一定比例时不再接受新的数据;

3、使用主从备份,或bgsave持久化(RDB方式无此问题),关闭AOF持久化,等下一版本发布。

4、升级3.2.9 版本。

2.动码攻击:当时IP统计、手机统计 都在一个redis集群。后面对于频次统计迁移到一个统计集群。不存明细

3.很多key没有设置过期时间,通过scan命令去ttl

4.hash tag问题,误用了{g}前缀,导致该节点数据集中、读写集中

不要存储大key(NONE)

减少存储的字段(DONE)

通过映射,缩短字段(DONE)

    userMobileNo          u

    deviceId                  d

    tag_first_withdraw   A001

业务上精简数据(FAIL)

    只存储7天的数据

分段hash(TODO)

————————————————

8、Java GC机制?GC Roots有哪些?。

9、对方法区和永久区的理解以及它们之间的关系?

建议读一下<深入理解java虚拟机>这本书.这里面是这样阐述方法区的:用于存储已经被JVM加载的类信息,常量,静态变量等.虽然JVM规范把方法区描述为堆的一个逻辑部分,

但是他却有另外的一个别名叫做Non-Heap(非堆),目的是个JVM的方法区分开来.很多人吧方法区称为永久代,但是两者其实并不等价.永久代只是来实现方法区而已,

这样HotSpot可以像管理java的堆内存一样管理这部分的内存,能够省去专门为方法区编写内存代码管理的工作.我觉得读完这段话,应该能明白方法区和永久代的关系.

我认为方法区使用的是JVM的堆内存,但是JVM团队想区分开来,所以有重新起名叫做方法区,那么方法区和永久代到底是很么关系,上面的一段话其实已经说明了,

方法区和永久代本质上不是等号的,永久代只是为了实现方法区,使得JVM可以通过像管理堆内存一样去实现管理方法区的GC机制.

元空间在取代永久代之后,唯一的不同之处在于,元空间所占用的内存为本地内存,不在是JVM申请的内存,所以只要机器还有内存,元空间就可以一直申请使用。

10、Spring怎样解决循环依赖的问题?

11、分布式缓存有哪些技术框架

12、如何解决线上问题,步骤和解决办法(运维知识)

13、除开gdb以外,有哪些服务端调试手段

14、hash索引的实现原理

总结一下 AVL 树的优点:

不错的查找性能(O(logn)),不存在极端的低效查找的情况。

可以实现范围查找、数据排序。

看起来 AVL 树作为数据查找的数据结构确实很不错,但是 AVL 树并不适合做 Mysql 数据库的索引数据结构,因为考虑一下这个问题:

数据库查询数据的瓶颈在于磁盘 IO,如果使用的是 AVL 树,我们每一个树节点只存储了一个数据,我们一次磁盘 IO 只能取出来一个节点上的数据加载到内存里,那比如查询 id=7 这个数据我们就要进行磁盘 IO 三次,

这是多么消耗时间的。所以我们设计数据库索引时需要首先考虑怎么尽可能减少磁盘 IO 的次数。

磁盘 IO 有个有个特点,就是从磁盘读取 1B 数据和 1KB 数据所消耗的时间是基本一样的,我们就可以根据这个思路,我们可以在一个树节点上尽可能多地存储数据,一次磁盘 IO 就多加载点数据到内存,这就是 B 树,B+树的的设计原理了

B 树和 B+树有什么不同呢?

第一,B 树一个节点里存的是数据,而 B+树存储的是索引(地址),所以 B 树里一个节点存不了很多个数据,但是 B+树一个节点能存很多索引,B+树叶子节点存所有的数据。

第二,B+树的叶子节点是数据阶段用了一个链表串联起来,便于范围查找。

通过 B 树和 B+树的对比我们看出,B+树节点存储的是索引,在单个节点存储容量有限的情况下,单节点也能存储大量索引,使得整个 B+树高度降低,减少了磁盘 IO。

其次,B+树的叶子节点是真正数据存储的地方,叶子节点用了链表连接起来,这个链表本身就是有序的,在数据范围查找时,更具备效率。

因此 Mysql 的索引用的就是 B+树,B+树在查找效率、范围查找中都有着非常不错的性能。

InnoDB一棵B+树可以存放多少行数据?

磁盘扇区(512字节)、文件系统(4kb)、InnoDB存储引擎(16kb)都有各自的最小存储单元。

----------------------------------------------------------------------------------

15、mysql的innoddb和MyIsam两种引擎的区别(没答上来)

1. InnoDB 支持事务,MyISAM 不支持事务。这是 MySQL 将默认存储引擎从 MyISAM 变成 InnoDB 的重要原因之一;

2. InnoDB 支持外键,而 MyISAM 不支持。对一个包含外键的 InnoDB 表转为 MYISAM 会失败;

3. InnoDB 是聚集索引,MyISAM 是非聚集索引。聚簇索引的文件存放在主键索引的叶子节点上,

因此 InnoDB 必须要有主键,通过主键索引效率很高。但是辅助索引需要两次查询,先查询到主键,然后再通过主键查询到数据。

因此,主键不应该过大,因为主键太大,其他索引也都会很大。而 MyISAM 是非聚集索引,数据文件是分离的,索引保存的是数据文件的指针。主键索引和辅助索引是独立的。

4. InnoDB 不保存表的具体行数,执行 select count(*) from table 时需要全表扫描。而MyISAM 用一个变量保存了整个表的行数,执行上述语句时只需要读出该变量即可,速度很快;   

5. InnoDB 最小的锁粒度是行锁,MyISAM 最小的锁粒度是表锁。一个更新语句会锁住整张表,导致其他查询和更新都会被阻塞,因此并发访问受限。这也是 MySQL 将默认存储引擎从 MyISAM 变成 InnoDB 的重要原因之一

16、mysql数据库索引的实现原理

17、一台机器只有1G物理内存,是否程序可以获取到2G,为什么。

18、描述,操作系统虚拟内存是如何实现的

用户进程向操作系统发出内存申请请求

系统会检查进程的虚拟地址空间是否被用完,如果有剩余,给进程分配虚拟地址

系统为这块虚拟地址创建的内存映射(Memory Mapping),并将它放进该进程的页表(Page Table)

系统返回虚拟地址给用户进程,用户进程开始访问该虚拟地址

CPU 根据虚拟地址在此进程的页表(Page Table)中找到了相应的内存映射(Memory Mapping),但是这个内存映射(Memory Mapping)没有和物理内存关联,于是产生缺页中断

操作系统收到缺页中断后,分配真正的物理内存并将它关联到页表相应的内存映射(Memory Mapping)。中断处理完成后 CPU 就可以访问内存了

当然缺页中断不是每次都会发生,只有系统觉得有必要延迟分配内存的时候才用的着,也即很多时候在上面的第 3 步系统会分配真正的物理内存并和内存映射(Memory Mapping)进行关联。

19、vector扩容的原理

20、什么环节会发送RST包

21、垃圾回收、redis、异常错误捕获、消息队列、kafka日志框架

22、秒杀系统怎么设计,性能优化(网站反应很慢 怎么优化)

23、怎么保证分布式数据的一致性 项目经验

24、redis数据结构 持久化方式 和高峰期如何避免雪崩、主从同步。

run-to-complete 模型、epoll 网络模型

25、唯一索引和key索引。HTTP协议相关。如何实现一个hash数据结构。

26、io模型

27、tcp粘包问题

28、web漏洞

29、http2.0有那些特性

30、io复用select,poll,epoll区别

31、es,zk,etcd,kafka了解?

总的来说Kafka快的原因:

1、partition顺序读写,充分利用磁盘特性,这是基础;

2、Producer生产的数据持久化到broker,采用mmap文件映射,实现顺序的快速写入;

3、Customer从broker读取数据,采用sendfile,将磁盘文件读到OS内核缓冲区后,直接转到socket buffer进行网络发送。

mmap 和 sendfile总结

1、都是Linux内核提供、实现零拷贝的API;

2、sendfile 是将读到内核空间的数据,转到socket buffer,进行网络发送;

3、mmap将磁盘文件映射到内存,支持读和写,对内存的操作会反映在磁盘文件上。

RocketMQ 在消费消息时,使用了 mmap。kafka 使用了 sendFile。

32、分布式锁怎么做?

33、负载均衡怎样做

34、tcp,项目,解决过的难点问题,思路过程

35、同步异步

36、进程线程

37、Redis 和 数据库

38、分布式架构拿nginx来讲的

39、进程间通信

40、zookeeper,kafka的知识,日志处理的架构,分布式系统的基本要素啥的。 

41、Threadlocal

42、web server如何实现大文件的上传下载

43、用sed替换某一行的某个词  sed -i '1s/xxx/ddd' file

44、kill命令的作用,不同选项下有什么区别

45、10秒频控,在10秒发起10万请求,在第11秒发起10万请求,这样我就在2秒内发起了20万请求,你如何解决?降低时间窗口期,10秒的窗口期换成2秒。

46、ads如何做分库访问db的。

47、走主键 一次io

48、走联合索引 两次io

49、不走索引 多次io

50、如果让你自己来实现分库分表你怎么做?

51、在32gb的内存里对1tb数据排序

52、hadoop和hive是做什么的,hive的udf是什么,一张大表和一张小表join,该如何做效率更高?

53、Cassandra的是如何存储的?为什么不用mysql?二者的区别。

54、elastic search如何存储,如何查询,其中做了什么优化?什么是倒排索引?

55、数据库,mysql和Postgre的事务是如何实现的。

56、Postgre和mysql数据库的区别是什么。

57、32位的系统,最多可以new出多大的内存?

58、32位的系统,最多可以创建多少的线程?

59、什么进程kill -9杀不掉(处于D状态的进程杀不掉)。

60、跨国的专线(高时延,大带宽)的TCP通讯如何优化?

61、设置一个合理的发送和接收缓冲区(带宽时延乘积)

62、调大初始拥塞控制窗口大小

63、挑战一致性hash扩容过程,配额服务扩容如何做到平滑,对业务不影响,或者业务不感知。

64、进程间通讯有哪些机制?那种速度最快?为什么?

65、cap定理、base理论、Redis集群

66、微服务和soa的区别

67、ThreadLocal用来干啥的,底层实现原理

3. 常见数据结构算法题

1、N个人中至少两个人同一天生日的概率

2、整数数组中找出前边的数都比自己小,后边的数都比自己大的数

3、字符串串去空格

4、手写链表反转函数,要求是不用其他辅助的存储

5、二叉树的遍历

6、计算二叉树高度

7、字符串转整形 

8、写一个lru算法

9、手写算法 :多文件内容排序,求根号算法

10、排序 时间复杂度

11、红黑树、二叉树、冒泡排序、快速排序

12、给定一个有序的旋转数组,查找某个元素是否在数组内,eg: [4 5 6 1 2 3]target=5

13、 给定一个物品在各个时间点的价格,求进行一次买卖之后最大获利。

变形为:求多次买卖的最大获利

14、 设计一个最优时空复杂度的位循环队列

15、实现一个LRU

16、系统为某进程分配了4个页框,该进程已访问的页号序列为2,0,2,9,3,4,2,8,2,4,8,4,5。若进程要访问的下一页的页号为7,依据LRU算法,应淘汰页的页号是几号?

17、手写深拷贝实现代码

18、哈夫曼编码

19、有序数组,相同的元素最多保留2个,O(1)的算法

20、手写代码,求链表倒数第k个元素

21.手写代码,求数组中出现次数超过一般的数

22. 有一个随机数生成器,能以p概率生成0,1-p概率生成1。利用这个生成器等概率生成0-6。

23. 在32gb的内存里对1tb数据排序

24. 简单动态规划,爬楼梯问题

25、最长公共子序列

26、二维数组,回字型输出

27、一个数组,和sum,求出数组中所有可能的任意元素(一个元素只能用一次)的和等于sum的组合。

28、柱状图求最大矩形面积

29、大整数字串相乘

30、N*N地图随机放X个雷,打印地图,每格显示周边雷的个数

有没有一种一定能保证线程安全的代码写法?(偷偷告诉你,真的有!)

多个线程如何保持A1B2C3等顺序交替输出?

synchronized volatile的CPU原语是如何实现的?

无锁、偏向锁、轻量级锁、重量级锁有什么差别?

如何正确的启动和停止一个线程?

线程和纤程的区别的是什么?为什么纤程比较轻量级?

ThreadLocal有没有内存泄漏的问题?为什么?

下列三种业务,应该如何使用线程池:

高并发、任务执行时间短

并发不高、任务执行时间长

并发高、业务执行时间长

+2,来看看这些问题吧

01 支付宝一面

介绍一下自己。

项目参与的核心设计有哪些

ArrayList和LinkedList底层

HashMap及线程安全的ConcurrentHashMap,以及各自优劣势

Java如何实现线程安全

Synchronized和Lock哪个更好?

HashMap中的get()方法是如何实现的?

HashMap可以用在哪些场景?

JVM,垃圾回收机制,内存划分等

SQL优化,常用的索引?

还有什么问题需要问的。

02 支付宝二面

没有自我介绍,直接问做过哪些Java开发相关的项目。

对哪些技术比较熟悉?

多线程状态图,状态如何流转?

死锁,死锁原因

页锁、乐观锁、悲观锁?

乐观锁如何保证线程安全?

用过线程池吗,对应的好处,如何用?

两个10G的文件,里面是一些url,内存只有1G,如何将这两个文件合并,找到相同的url?

1000个多并发线程,10台机器,每台机器4核的,设计线程池大小。

代码题:两个有序数组,数组中存在重复数字,合并成一个有序数组,去除重复数字。

说一下自己的优点。

03 支付宝三面

jvm性能调优都做了什么

数据库性能调优如何做

分布式系统原理:CAP,最终一致性,幂等操作等

高并发情况下,我们系统是如何支撑大量的请求的

集群如何同步会话状态

常用NOSQL,有做过比较?

什么情况会出现雪崩,以及如何应对?

负载均衡的原理

数据库事务属性

与同事沟通的时候,如果遇到冲突了如何解决?

工作中觉得哪方面欠缺?

有问题要问么?

期望薪水?

其实结合面试题,大家不难看出,题目基本涵盖的方面就那一些,最主要的, 多线程与高并发,jvm、设计模式、redis,zookeeper,数据库 这6个模块,再接下来就是一些具体项目的应用

1.二叉搜索树和平衡二叉树有什么关系,强平衡二叉树(AVL树)和弱平衡二叉树(红黑树)有什么区别

二叉搜索树:也称二叉查找树,或二叉排序树。定义也比较简单,要么是一颗空树,要么就是具有如下性质的二叉树:

(1)若任意节点的左子树不空,则左子树上所有结点的值均小于它的根结点的值;(

2)若任意节点的右子树不空,则右子树上所有结点的值均大于它的根结点的值;

(3)任意节点的左、右子树也分别为二叉查找树;

(4)没有键值相等的节点。平衡二叉树:在二叉搜索树的基础上多了两个重要的特点

(1)左右两子树的高度差的绝对值不能超过1;

(2)左右两子树也是一颗平衡二叉树。

红黑书:红黑树是在普通二叉树上,对每个节点添加一个颜色属性形成的,需要同时满足一下五条性质

(1)节点是红色或者是黑色;

(2)根节点是黑色;

(3)每个叶节点(NIL或空节点)是黑色;

(4)每个红色节点的两个子节点都是黑色的(也就是说不存在两个连续的红色节点);

(5)从任一节点到其没个叶节点的所有路径都包含相同数目的黑色节点。

区别:AVL树需要保持平衡,但它的旋转太耗时,而红黑树就是一个没有AVL树那样平衡,因此插入、删除效率会高于AVL树,而AVL树的查找效率显然高于红黑树。

B树和B+树的区别,为什么MySQL要使用B+树B树:

(1)关键字集合分布在整颗树中;(2)任何一个关键字出现且只出现在一个结点中;(3)搜索有可能在非叶子结点结束;(4)其搜索性能等价于在关键字全集内做一次二分查找;

B+树:

(1)有n棵子树的非叶子结点中含有n个关键字(b树是n-1个),这些关键字不保存数据,只用来索引,所有数据都保存在叶子节点(b树是每个关键字都保存数据);

(2)所有的叶子结点中包含了全部关键字的信息,及指向含这些关键字记录的指针,且叶子结点本身依关键字的大小自小而大顺序链接;

(3)所有的非叶子结点可以看成是索引部分,结点中仅含其子树中的最大(或最小)

关键字;(4)通常在b+树上有两个头指针,一个指向根结点,一个指向关键字最小的叶子结点;

(5)同一个数字会在不同节点中重复出现,根节点的最大元素就是b+树的最大元素。B+树相比于B树的查询优势:

(1)B+树的中间节点不保存数据,所以磁盘页能容纳更多节点元素,更“矮胖”;

(2)B+树查询必须查找到叶子节点,B树只要匹配到即可不用管元素位置,因此B+树查找更稳定(并不慢);

(3)对于范围查找来说,B+树只需遍历叶子节点链表即可,B树却需要重复地中序遍历参考文章:

如何解决Hash冲突通过引入单向链表来解决Hash冲突。当出现Hash冲突时,比较新老key值是否相等,如果相等,新值覆盖旧值。如果不相等,新值会存入新的Node结点,

指向老节点,形成链式结构,即链表。当Hash冲突发生频繁的时候,会导致链表长度过长,以致检索效率低,所以JDK1.8之后引入了红黑树,

当链表长度大于8时,链表会转换成红黑书,以此提高查询性能。

epoll和poll的区别,

及其应用场景select和epoll都是I/O多路复用的方式,但是select是通过不断轮询监听socket实现,e

poll是当socket有变化时通过回掉的方式主动告知用户进程实现参考文章:https://www.cnblogs.com/hsmwlyl/p/10652503.html5.

简述线程池原理,FixedThreadPool用的阻塞队列是什么?Java线程池的实现原理其实就是一个线程集合workerSet和一个阻塞队列workQueue。

当用户向线程池提交一个任务(也就是线程)时,线程池会先将任务放入workQueue中。workerSet中的线程会不断的从workQueue中获取线程然后执行。

当workQueue中没有任务的时候,worker就会阻塞,直到队列中有任务了就取出来继续执行。FixedThreadPool使用的是“无界队列”LinkedBlockingQueue

sychronized和ReentrantLock的区别(1)ReentrantLock显示获得、释放锁,synchronized隐式获得释放锁(2)ReentrantLock可响应中断、可轮回,synchronized是不可以响应中断的,为处理锁的不可用性提供了更高的灵活性

(3)ReentrantLock是API级别的,synchronized是JVM级别的(4)ReentrantLock可以实现公平锁(5)ReentrantLock通过Condition可以绑定多个条件

sychronized的自旋锁、偏向锁、轻量级锁、重量级锁,分别介绍和联系自旋锁:

果持有锁的线程能在很短时间内释放锁资源,那么那些等待竞争锁的线程就不需要做内核态和用户态之间的切换进入阻塞挂起状态,它们只需要等一等(自旋),

等持有锁的线程释放锁后即可立即获取锁,这样就避免用户线程和内核的切换的消耗。偏向锁:顾名思义,它会偏向于第一个访问锁的线程,如果在运行过程中,

同步锁只有一个线程访问,不存在多线程争用的情况,则线程是不需要触发同步的,减少加锁/解锁的一些CAS操作(比如等待队列的一些CAS操作),

这种情况下,就会给线程加一个偏向锁。如果在运行过程中,遇到了其他线程抢占锁,则持有偏向锁的线程会被挂起,JVM会消除它身上的偏向锁,将锁恢复到标准的轻量级锁。

轻量级锁:轻量级锁是由偏向所升级来的,偏向锁运行在一个线程进入同步块的情况下,当第二个线程加入锁争用的时候,偏向锁就会升级为轻量级锁;重量级锁:我们知道,

我们要进入一个同步、线程安全的方法时,是需要先获得这个方法的锁的,退出这个方法时,则会释放锁。如果获取不到这个锁的话,意味着有别的线程在执行这个方法,

这时我们就会马上进入阻塞的状态,等待那个持有锁的线程释放锁,然后再把我们从阻塞的状态唤醒,我们再去获取这个方法的锁。这种获取不到锁就马上进入阻塞状态的锁,

我们称之为重量级锁。

HTTP有哪些问题,加密算法有哪些,针对不同加密方式可能产生的问题,及其HTTPS是如何保证安全传输的HTTP的不足:

通信使用明文,内容可能会被窃听;不验证通信方的身份,因此有可能遭遇伪装;无法证明报文的完整性,有可能已遭篡改;

常用加密算法:MD5算法、DES算法、AES算法、RSA算法

蚂蚁Java二面

1.设计模式有哪些大类,及熟悉其中哪些设计模式创建型模式、结构型模式、行为型模式

volatile关键字,他是如何保证可见性,有序性volatile可以保证线程可见性且提供了一定的有序性,

但是无法保证原子性。在JVM底层volatile是采用“内存屏障”来实现的。观察加入volatile关键字和没有加入volatile关键字时所生成的汇编代码发现,

加入volatile关键字时,会多出一个lock前缀指令,lock前缀指令实际上相当于一个内存屏障(也成内存栅栏),

内存屏障会提供3个功能:I.它确保指令重排序时不会把其后面的指令排到内存屏障之前的位置,

也不会把前面的指令排到内存屏障的后面;即在执行到内存屏障这句指令时,在它前面的操作已经全部完成;

II.它会强制将对缓存的修改操作立即写入主存;

III.如果是写操作,它会导致其他CPU中对应的缓存行无效。

Java的内存结构,堆分为哪几部分,

默认年龄多大进入老年代Java的内存结构:程序计数器、虚拟机栈、本地方法栈、堆、方法区。Java虚拟机根据对象存活的周期不同,把堆内存划分为几块,一般分为新生代、老年代和永久代。

默认的设置下,当对象的年龄达到15岁的时候,也就是躲过15次Gc的时候,他就会转移到老年代中去躲过15次GC之后进入老年代。

4.ConcurrentHashMap如何保证线程安全,jdk1.8有什么变化JDK1.7:使用了分段锁机制实现ConcurrentHashMap,ConcurrentHashMap在对象中保

存了一个Segment数组,即将整个Hash表划分为多个分段;而每个Segment元素,即每个分段则类似于一个Hashtable;这样,在执行put操作时首先根据hash算法定位到元素属于哪个Segment,然后对该Segment加锁即可。

因此,ConcurrentHashMap在多线程并发编程中可是实现多线程put操作,不过其最大并发度受Segment的个数限制。JDK1.8:底层采用数组+链表+红黑树的方式实现,

而加锁则采用CAS和synchronized

为什么ConcurrentHashMap底层为什么要红黑树因为发生hash冲突的时候,会在链表上新增节点,但是链表过长的话会影响检索效率,引入红黑书可以提高插入和查询的效率。

6.如何做的MySQL优化MySQL的优化有多种方式,我们可以从以下几个方面入手:存储引擎的选择、字段类型的选择、索引的选择、分区分表、主从复制、读写分离、SQL优化。

讲一下oom以及遇到这种情况怎么处理的,是否使用过日志分析工具OOM,全称“OutOfMemory”,翻译成中文就是“内存用完了”,

当JVM因为没有足够的内存来为对象分配空间并且垃圾回收器也已经没有空间可回收时,就会抛出这个error。

处理过程:首先通过内存映射分析工具如EclipseMemoryAnalyzer堆dump出的异常堆转储进行快照解析确认内存中的对象是否是必要的,

也就是先分清楚是内存泄漏MemoryLeak还是MemoryOverflow如果是内存泄漏可通过工具进一步查看泄露的对象到GCRoots的引用链,

就能找到泄露对象是怎么通过路径与GCRoots相关联导致垃圾收集器无法回收他们如果不存在泄露就检查堆参数-Xmx与-Xms与机器物理内存对比是否还可以

调大从代码上检测是否是某些对象的生命周期过长持有状态时间过长尝试减少代码运行期间的内存消耗

蚂蚁Java三面

1.项目介绍

2.你们怎么保证Redis缓存和数据库的数据一致性?可以通过双删延时策略来保证他们的一致性。

3.Redis缓存雪崩?击穿?穿透?缓存雪崩:缓存同一时间大面积的失效,所以,后面的请求都会落到数据库上,

造成数据库短时间内承受大量请求而崩掉。缓存击穿:key对应的数据存在,但在redis中过期,此时若有大量并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,

这个时候大并发的请求可能会瞬间把后端DB压垮。缓存穿透:key对应的数据在数据源并不存在,每次针对此key的请求从缓存获取不到,请求都会到数据源,从而可能压垮数据源。

比如用一个不存在的用户id获取用户信息,不论缓存还是数据库都没有,若黑客利用此漏洞进行攻击可能压垮数据库。

4.你熟悉哪些消息中间件,有做过性能比较?RocketMQ、RabbitMQ、ActiveMQ、Kafka

你可能感兴趣的:(技术专家总结分析)