蚂蚁Java一面 1. 二叉搜索树和平衡二叉树有什么关系,强平衡二叉树(AVL树)和弱平衡二叉树(红黑树)有什么区别 二叉搜索树:也称二叉查找树,或二叉排序树。定义也比较简单,要么是一颗空树,要么就是具备以下性质的二叉树: (1)若任意节点的左子树不空,则左子树上全部结点的值均小于它的根结点的值; (2)若任意节点的右子树不空,则右子树上全部结点的值均大于它的根结点的值; (3)任意节点的左、右子树也分别为二叉查找树; (4)没有键值相等的节点。 平衡二叉树:在二叉搜索树的基础上多了两个重要的特色 (1)左右两子树的高度差的绝对值不能超过1; (2)左右两子树也是一颗平衡二叉树。 红黑书:红黑树是在普通二叉树上,对每一个节点添加一个颜色属性造成的,须要同时知足一下五条性质 (1)节点是红色或者是黑色; (2)根节点是黑色; (3)每一个叶节点(NIL或空节点)是黑色; (4)每一个红色节点的两个子节点都是黑色的(也就是说不存在两个连续的红色节点); (5)从任一节点到其没个叶节点的全部路径都包含相同数目的黑色节点。 区别:AVL树须要保持平衡,但它的旋转太耗时,而红黑树就是一个没有AVL树那样平衡,所以插入、删除效率会高于AVL树,而AVL树的查找效率显然高于红黑树。 参考文章1:https://blog.csdn.net/qq_25940921/article/details/82183093 参考文章2:https://blog.csdn.net/yang_yulei/article/details/26066409 2. 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树却须要重复地中序遍历 参考文章:https://www.cnblogs.com/xueqiuqiu/articles/8779029.html 3. HashMap如何解决Hash冲突 经过引入单向链表来解决Hash冲突。当出现Hash冲突时,比较新老key值是否相等,若是相等,新值覆盖旧值。若是不相等,新值会存入新的Node结点,指向老节点,造成链式结构,即链表。 当Hash冲突发生频繁的时候,会致使链表长度过长,以至检索效率低,因此JDK1.8以后引入了红黑树,当链表长度大于8时,链表会转换成红黑书,以此提升查询性能。 参考文章:https://blog.csdn.net/qedgbmwyz/article/details/79908333 4. epoll和poll的区别,及其应用场景 select和epoll都是I/O多路复用的方式,可是select是经过不断轮询监听socket实现,epoll是当socket有变化时经过回掉的方式主动告知用户进程实现 参考文章:https://www.cnblogs.com/hsmwlyl/p/10652503.html 5. 简述线程池原理,FixedThreadPool用的阻塞队列是什么? Java线程池的实现原理其实就是一个线程集合workerSet和一个阻塞队列workQueue。当用户向线程池提交一个任务(也就是线程)时,线程池会先将任务放入workQueue中。 workerSet中的线程会不断的从workQueue中获取线程而后执行。当workQueue中没有任务的时候,worker就会阻塞,直到队列中有任务了就取出来继续执行。 FixedThreadPool使用的是“无界队列”LinkedBlockingQueue 参考文章:https://blog.csdn.net/wanghao112956/article/details/99938893 6. sychronized和ReentrantLock的区别 (1)ReentrantLock显示得到、释放锁,synchronized隐式得到释放锁 (2)ReentrantLock可响应中断、可轮回,synchronized是不能够响应中断的,为处理锁的不可用性提供了更高的灵活性 (3)ReentrantLock是API级别的,synchronized是JVM级别的 (4)ReentrantLock能够实现公平锁 (5)ReentrantLock经过Condition能够绑定多个条件 参考文章:https://blog.csdn.net/zxd8080666/article/details/83214089 7. sychronized的自旋锁、偏向锁、轻量级锁、重量级锁,分别介绍和联系 自旋锁:果持有锁的线程能在很短期内释放锁资源,那么那些等待竞争锁的线程就不须要作内核态和用户态之间的切换进入阻塞挂起状态,它们只须要等一等(自旋), 等持有锁的线程释放锁后便可当即获取锁,这样就避免用户线程和内核的切换的消耗。 偏向锁:顾名思义,它会偏向于第一个访问锁的线程,若是在运行过程当中,同步锁只有一个线程访问,不存在多线程争用的状况,则线程是不须要触发同步的,减小加锁/解锁 的一些CAS操做(好比等待队列的一些CAS操做),这种状况下,就会给线程加一个偏向锁。 若是在运行过程当中,遇到了其余线程抢占锁,则持有偏向锁的线程会被挂起,JVM会 消除它身上的偏向锁,将锁恢复到标准的轻量级锁。 轻量级锁:轻量级锁是由偏向所升级来的,偏向锁运行在一个线程进入同步块的状况下,当第二个线程加入锁争用的时候,偏向锁就会升级为轻量级锁; 重量级锁:咱们知道,咱们要进入一个同步、线程安全的方法时,是须要先得到这个方法的锁的,退出这个方法时,则会释放锁。若是获取不到这个锁的话,意味着有别的线程在 执行这个方法,这时咱们就会立刻进入阻塞的状态,等待那个持有锁的线程释放锁,而后再把咱们从阻塞的状态唤醒,咱们再去获取这个方法的锁。这种获取不到锁就立刻进入阻 塞状态的锁,咱们称之为重量级锁。 参考文章:https://blog.csdn.net/zqz_zqz/article/details/70233767 参考文章:https://www.cnblogs.com/myseries/p/10773078.html 8. HTTP有哪些问题,加密算法有哪些,针对不一样加密方式可能产生的问题,及其HTTPS是如何保证安全传输的 HTTP的不足: 通讯使用明文,内容可能会被窃听; 不验证通讯方的身份,所以有可能遭遇假装; 没法证实报文的完整性,有可能已遭篡改; 经常使用加密算法:MD5算法、DES算法、AES算法、RSA算法 参考文章:https://blog.csdn.net/baidu_22254181/article/details/82594072蚂蚁Java二面 1. 设计模式有哪些大类,及熟悉其中哪些设计模式 建立型模式、结构型模式、行为型模式 参考文章:http://c.biancheng.net/design_pattern/ 2. volatile关键字,他是如何保证可见性,有序性 volatile能够保证线程可见性且提供了必定的有序性,可是没法保证原子性。在JVM底层volatile是采用“内存屏障”来实现的。 观察加入volatile关键字和没有加入volatile关键字时所生成的汇编代码发现,加入volatile关键字时,会多出一个lock前缀指令, lock前缀指令实际上至关于一个内存屏障(也成内存栅栏),内存屏障会提供3个功能: I. 它确保指令重排序时不会把其后面的指令排到内存屏障以前的位置,也不会把前面的指令排到内 存屏障的后面;即在执行到内存屏障这句指令时,在它前面的操做已经所有完成; II. 它会强制将对缓存的修改操做当即写入主存; III. 若是是写操做,它会致使其余CPU中对应的缓存行无效。 参考文章:https://blog.csdn.net/summerZBH123/article/details/80547516 3. 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实现 参考文章:https://blog.csdn.net/weixin_44460333/article/details/86770169 5. 为何ConcurrentHashMap底层为何要红黑树 由于发生hash冲突的时候,会在链表上新增节点,可是链表过长的话会影响检索效率,引入红黑书能够提升插入和查询的效率。 6. 如何作的MySQL优化 MySQL的优化有多种方式,咱们能够从如下几个方面入手: 存储引擎的选择、字段类型的选择、索引的选择、分区分表、主从复制、读写分离、SQL优化。详细优化请查看参考文章 参考文章:https://blog.csdn.net/zls986992484/article/details/52860496 7. 讲一下oom以及遇到这种状况怎么处理的,是否使用过日志分析工具 OOM,全称“Out Of Memory”,翻译成中文就是“内存用完了”,当JVM由于没有足够的内存来为对象分配空间而且垃圾回收器也已经没有空间可回收时,就会抛出这个error。 处理过程:首先经过内存映射分析工具 如 Eclipse Memory Analyzer 堆 dump出的异常堆转储进行快照解析确认内存中的对象是不是必要的, 也就是先分清楚是 内存泄漏 Memory Leak 仍是Memory Overflow 若是是内存泄漏 可经过工具进一步查看泄露的对象到GC Roots的引用链, 就能找到泄露对象是怎么经过路径与 GC Roots 相关联致使垃圾收集器没法回收他们若是不存在泄露 就检查堆参数 -Xmx 与 -Xms 与机器物理 内存对比是否还能够调大 从代码上检测 是不是某些对象的生命周期过长持有状态时间过长 尝试减小代码运行期间的内存消耗。 参考文章:https://www.cnblogs.com/ThinkVenus/p/6805495.html蚂蚁Java三面 1. 项目介绍 2. 大家怎么保证Redis缓存和数据库的数据一致性? 能够经过双删延时策略来保证他们的一致性。 参考文章:https://blog.kido.site/2018/12/07/db-and-cache-02/ 3. Redis缓存雪崩?击穿?穿透? 缓存雪崩:缓存同一时间大面积的失效,因此,后面的请求都会落到数据库上,形成数据库短期内承受大量请求而崩掉。 缓存击穿:key对应的数据存在,但在redis中过时,此时如有大量并发请求过来,这些请求发现缓存过时通常都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。 缓存穿透:key对应的数据在数据源并不存在,每次针对此key的请求从缓存获取不到,请求都会到数据源,从而可能压垮数据源。好比用一个不存在的用户id获取用户信息,不论缓存仍是数据库都没有,若黑客利用此漏洞进行攻击可能压垮数据库。 4. 你熟悉哪些消息中间件,有作过性能比较? RocketMQ、RabbitMQ、ActiveMQ、Kafka 参考文章:https://blog.csdn.net/wqc19920906/article/details/82193316