2019年03月12日内推简历投递
2019年04月19日面完HR面
2019年04月30日收到录取意向书
部门:阿里集团-阿里妈妈事业群-阿里妈妈事业部-联盟业务
阿里一面(60min)(基础面)
1、Netty、多线程相关
他:讲一下TCP粘包原理,怎么解决。
他:讲一下Reactor线程模型。
他:Netty的线程池和普通的线程池相比,性能提高的原因。
他:讲一下用户态和内核态的区别。
他:Java可以用到内核态吗。
他:你刚才说Netty是事件驱动的,如何怎么理解事件驱动。
他:讲一下Java线程池有哪些参数,原理。
他:讲一下Synchronize和Lock的区别。
追问:讲一下AQS。
追问:讲一下CAS。
2、JVM虚拟机
他:讲一下JVM虚拟机的几个内存区域。
他:讲一下内存分代。
他:一定是8:1吗?可以调整这两个区域的大小吗?
他:是什么参数?
他:你知道些什么参数?
他:讲一下MinorGC和FullGC。
他:讲一下垃圾收集器。
追问:你刚才说CMS是并发标记并发清除,那CMS在并发收集的时候需要Stop The World吗?
追问:为什么要等所有线程执行到SafePoint呢?
他:讲一下内存分配有哪些规则。
他:Java的类加载器有哪些。
追问:项目中有自己实现过类加载器吗。
3、 Java基础语法和集合框架源码理解
他:hashcode()和equal()?
他:HashMap的put,他是怎样一个过程?
他:HashMap的load factor了解吗?
他:Java的受检异常和非受检异常有什么区别?
他:你知道有哪些异常吗,说一下平时遇到过哪些异常?
他:如果try里面return了,finally会执行吗?
他:如果finally里面还有异常,会怎么样?
他:其实我刚刚这个问题有点迷惑性,你回答还会继续抛出异常就可以了。
4、数据库
他:left join和right join的区别。
他:InnoDB和MyIsam的区别,这里我提到了MVCC。
追问:说一下对InnoDB的MVCC的理解。
追问:Update的时候MVCC是怎样的过程。
5、Spring
他:@Autowired和@Resource的区别。
他:说说你对Spring Boot的理解。
他:讲一下Spring Cloud的组件。
6、设计模式
他:说说你知道的设计模式。
他:观察者模式如何实现。
7、你最近在看哪些书?
8、有其他Offer吗?
9、有什么问题吗?
阿里二面(30min)(代码面)
下午甩给我一个链接,让我2小时内实现这个题目(要求多线程实现)文末附上当时写的代码,大佬反馈说写的太长了,有更简便的实现方法。
题目:按文件大小排序,统计一个目录下按文件大小从高到底排序的TOP20个文件。
输入:一个目录,如/home/
输出:按文件大小排序的TOP20个文件
一、题是你做的吗。
二、讲下解题思路。
三、解题过程中有没有遇到什么问题。
四、平时用什么工具开发,有用过IDEA的哪些插件。
五、平时怎么进行单元测试,用哪些工具。
六、有没有处理线上问题的经验。
七、如果面对一个高并发的业务,要做哪些考虑呢。
八、做过几个Java后端项目。
九、上次面试学到了什么。
十、今天面试学到了什么。
十一、对你而言最有意义的一本书是什么。
阿里三面(70min)(项目面)
这一面主要聊了项目,所以持续时间比较久
一、聊一下项目,结构、规模、并发量、技术原理等角度。
二、你的项目的大小扩充一千倍,你觉得系统性能瓶颈在哪里,架构要怎么变化。
三、讲一下浏览器访问一个网站的全过程。
四、数据库的隔离性、锁、锁带来的问题。
五、给定两个文件,文件里面有每行一个数,求交集。(数据量10G,内存1G)
阿里四面(80min)(这一面侧重你平时的学习方式、思考习惯,夹杂基础)
一、要搭建一个高性能网站,你会采取哪些方式?
他:你讲了很多总结起来就是负载均衡和缓存,还有别的吗,能不能总结性地给出一些方案,不需要深入介绍。
二、要搭建一个高可用性网站,你会采用哪些方式?
三、你对操作系统、计算机网络这些都知道的吧?那我问你一个基础的啊,你讲一下操作系统有哪些内存分配算法。
PS:这里我讲了LRU、FIFO、时钟置换,但是讲的磕磕碰碰,很慢,我说前两天还刚手写过一个LRU并发插入和查找O(1)的数据结构,大佬不太满意,问我还有没有别的,常用的。我就想不起来了,他问我是不是没准备操作系统,我说是,其他公司也很少问到这个知识点,如果要用的话,我看一眼就能捡起来,他说但是我们招同学就是希望基础更为扎实的,我说操作系统一些概念的理解我还是到位的,他也没有继续问了。
四、设计模式有哪些原则?能不能做一个总结性的介绍?
PS:这个题我也是慢吞吞地答了5个设计原则,第6个真想不起来了,气氛就特别尴尬,给他的感觉是我的基础不扎实吧。他说远远不止6大设计原则,其实还有很多其他的。我说我没有背过,他说这不是背,而是理解记忆。
这里简单做一个总结
1.单一职责原则:这个类尽量只负责一个功能,也就是说,引起该类发生变化的原因只有一个
2.开闭原则:面对扩展开放,面对修改关闭
3.迪米特法则(最小知识原则):依赖尽量少的类
4.依赖倒置原则:程序依赖于抽象,而不依赖于具体实现
5.里式替换原则:基类可以出现的地方,子类一定可以出现
6.接口隔离原则:一个类对另外一个类的依赖性应当是建立在最小的接口上
五、你刚才说设计模式,你知道哪些设计模式?能不能做一个总结性的介绍?
单例、简单工厂、工厂方法、抽象工厂、观察者、命令、装饰器、迭代器、策略、模板方法、适配器等。
六、设计模式在你的项目中有哪些应用?
在游戏项目中,用过工厂模式、单例模式、装饰器模式
在Spring Cloud中,Feign这个组件是用动态代理的方式实现的
在Spring中,创建bean的时候用了单例模式,AOP用了动态代理模式,ApplicationContext用了工厂模式。
在Java中,Iterator用了迭代器模式,String常量池和Integer.valueOf等缓存策略,用了享元模式,IO相关类(InputStream、OutputStream)用了装饰器模式,StringBuffer、StringBuilder用了建造者模式。
七、你觉得设计模式是为了做什么的?
八、对Hibernate和Mybatis做个比较。
九、你Spring、Spring Boot、Spring MVC、Spring Cloud这些东西都用过,那给我来一个总结性的介绍吧。然后又觉得说的太多了,改口说对比一下Spring Boot和Spring的区别吧。
整体来看,Spring MVC和Spring Boot都属于Spring,Spring MVC 是基于Spring的一个 MVC 框架,而Spring Boot 是基于Spring的一套快速开发整合包。
有关Spring和Spring Boot的对比
1.Spring需要做很多配置,定义bean就包括三种方式(xml、注解、Java代码),而Spring Boot尽可能自动做一些配置。
2.Spring Boot内置Tomcat、Jetty等容器,就不需要去部署了,Spring则需要。
3.Spring Boot提供pom文件简化配置,当我们引入核心依赖时,会自动引入其他依赖。
十、1T文件128G内存求TOP10频率热词。
十一、说说你对面向对象、面向过程等的理解,你觉得他对于开发人员来说有什么意义。
阿里五面(44min)(侧重技术广度和深度)
你知道哪些数据库?对比一下
讲了Redis、Memcache、MongoDB、Mysql
追问:你们用的Redis的集群,怎么做的?
我:Redis的集群有三种方式实现,我用的是官网的那种,不需要插件,直接配置的。
追问:说了一些我听不懂的话
我:不知道
你知道Elastic Search之类的数据库吗?
讲了一下大数据实验时候用过,原理不知道
你知道RabbitMQ、Kafka、RocketMQ、ActiveMQ吗?
没有对比过
你为什么选择RabbitMQ?
就看着挑了一个
你对机器学习算法有了解吗?
我讲到决策树,按照信息增益选择特征作为节点进行分裂。
信息增益和熵是一个东西吗?
不知道
你知道有哪些回归器?
决策树、随机森林、Xgboost、朴素贝叶斯等,具体原理不了解,只是应用,调参。
你觉得你有什么亮点?
我觉得我对Mysql的InnoDB比较熟悉
你讲一下Mysql的InnoDB的原理吧
讲了索引、锁、redo log、undo log、插入缓冲
你对操作系统底层的锁是怎么实现有了解吗?
我知道的是操作系统是通过信号量、PV操作来实现的,临界区巴拉巴拉。
我不知道这算不算底层。
还有别的吗?你知道一个sql语句的在存储引擎层面的解析过程吗?
我不知道。
之前数据结构都没怎么问,你对数据结构和算法哪些比较熟悉?
链表、二叉树、图(深搜广搜最短路)、字符串的KMP等算法书上有的都知道吧
你平时二叉树哪些写的多?
普通的二叉树、查找二叉树、平衡二叉树、B树、B+树、前缀树、红黑树,红黑树没有实现过,其他的都有实现或者看过代码。
讲一下AVL、B树、B+树
AVL是高度平衡,巴拉巴拉
B树的分裂巴拉巴拉
B+树叶子节点之间有链表
还有别的吗
还有一个项目之前没聊过的可以聊聊,巴拉巴拉
好的,就到这里把,你还有什么问题吗
HR面(27min)
讲下你的项目,不用描述技术细节
聊下你的职业规划?
你有哪些爱好?
最近看的电影是什么?
你觉得你的优点和缺点是什么?
有其他Offer吗?
你的话应该没什么问题,过几天可能会发Offer
下面那道多线程题的一个题解,细节还可以改进。
package tx;
import java.io.File;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.PriorityBlockingQueue;
public class Main {
public static File[] filelist = new File[100000]; //存放文件
public static int idx = 0; //统计总文件数量
public static List res = new ArrayList<>(); //存放结果
public static int block = 10000; //拆分块的大小
public static int k = 20; //找topK
public static CountDownLatch latch;
public static PriorityBlockingQueue priorityBlockingQueue = new PriorityBlockingQueue(k, new cmp());
public static class cmp implements Comparator {
@Override
public int compare(File f1, File f2) {
if (f1.length() > f2.length())
return -1;
else if (f1.length() < f2.length())
return 1;
else
return 0;
}
}
public static void main(String[] args) {
//1.递归搜索文件路径,找出所有文件并保存
getFileList("/Users/wjq/Desktop");
//2.拆分文件成为num块,开num个线程去处理
int num = idx / block;
latch = new CountDownLatch(num);
for (int i = 0; i < num; i++) {
int start = i * block, end = Math.min((i + 1) * block - 1, idx - 1);
File[] tempF = new File[Math.min(block, end - start + 1)];
for (int k = 0, j = start; j <= end; j++, k++)
tempF[k] = filelist[j];
Thread mythread = new MyThread(tempF, 0, block - 1);
mythread.start();
}
System.out.println("所有线程已经完成工作");
try {
latch.await();
} catch (Exception e) {
e.printStackTrace();
}
//3.输出结果
for (int i = 0; i < k; i++) {
File curFile = priorityBlockingQueue.poll();
System.out.println(curFile.getName() + " : " + curFile.length());
}
}
public static void getFileList(String strPath) {
File[] files = new File(strPath).listFiles();
if (files == null)
return;
for (int i = 0; i < files.length; i++) {
if (files[i].isDirectory())
getFileList(files[i].getAbsolutePath());
else
filelist[idx++] = (files[i]);
}
}
public static class MyThread extends Thread {
private int lo;
private int hi;
private File[] file;
public MyThread(File[] file, int lo, int hi) {
this.file = file;
this.lo = lo;
this.hi = hi;
}
public void run() {
try {
while (lo < hi) {
int onePartition = partition(file, lo, hi);
if (onePartition == k) {
break;
} else if (onePartition > k) {
hi = onePartition - 1;
} else if (onePartition < k) {
lo = onePartition + 1;
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
for (int i = 0; i < k; i++) {
priorityBlockingQueue.add(file[i]);
}
latch.countDown();
}
}
public int partition(File[] nums, int start, int end) {
int index = start;
File endFile = nums[end];
for (int i = start; i < end; i++) {
if (nums[i].length() > nums[end].length()) {
//交换两个file的位置
File temp = nums[i];
nums[i] = nums[index];
nums[index] = temp;
index++;
}
}
File temp = nums[index];
nums[index] = endFile;
nums[end] = temp;
return index;
}
}
}