小米面试总结(附答案)

版权声明:本文为博主原创文章,转载时请在文章最前方附上本文地址。 https://blog.csdn.net/qq_35033270/article/details/88734093

1.自我介绍,包括自己做的项目、技术栈、遇到的问题以及怎么解决的。

与前文类似,略

2.怎么解决某一时刻,生产者速率逐增,消费者速率不够的问题。如何保证消息不丢失,持久化到数据库那如何解决集群重复消费问题,消息中间件对比。

消费者消费速率某一时刻跟不上的问题:我的想法是在条件允许的情况下,增加消费者数量及调大jvm堆内存,面试官后续又限制了条件,即不可调优。我则提出了引进消息组件activeMq,它的适用场景除了解耦异步,还可削峰,就面试官而言我觉得他对这个做法勉强接受,他的本意应该是让我自己设计一种策略来解决这种问题。事后我查了下,除了引进消息队列,我暂时没什么好的思路。后续有思路了再更新。
如何保证消息不丢失:我的思路1是:设置任务状态,持久化入库,如果服务down了,后续启动还是查询数据库进行任务的再处理,然后面试官又提了个问题:如何解决集群下的重复消费?我的思路2即:还是引进消息组件activeMq,这样点对点模式,这样即不会丢失消息,也不会重复消费。
消息中间件对比:这个我只大概说出了量级的区别,以及稳定性方面。之前大概看了些,基本都忘了。具体可参考:消息中间件(一)MQ详解及四大MQ比较

3.websocket的底层,它的长连接和TCP的长连接有什么区别,为什么不直接shiyongTCP

websocket是基于TCP的一种全双工通信协议。包含初始的握手过程,以及后续的多次数据帧双向传输过程。其目的是解决通信双方的频繁通信请求,以避免多个连接建立的资源损耗,提高工作效率和资源利用率。
websocket是应用层,TCP是网络层。基于tcp的socket协议是针对c/s模式,无法满足b/s模式,而websocket可满足b/s模式,由浏览器发起请求。

4.自己在工作中看过那些底层源码的底层原理,hashmap底层数组变化,如何扩容,扩容怎么实现的

底层实现原理:数组+链表,其主干是Entry数组,包含key-value键值对。数组是hashmap的主体,链表是为了解决哈希冲突。对于查找和添加而言,若定位到位置不含链表,则查找很快,仅需一次寻址即可。如果含有链表,则遍历链表,存在即覆盖,否则新增。对于查找而言,则需要遍历链表,依次通过对key对象的equals方法进行逐一对比查找。就性能考虑,链表越少越好。
关于不同版本的hashmap差异:在jdk1.7及之前,是数组+链表。在jdk8,也是数组+链表,不过对链表处进行了优化,若链表长度大于8,则将链表转为红黑树。
当hashmap的元素值大于负载因子,即会数组扩容,hashmap的负载因子loadfactor默认是0.75,举例说明下:当hashmap容量为16,则当hashmap内部元素超过16*0.75=12,也就是超过12时,会进行数组扩容,扩容为扩大一倍,有一点需要注意的是hashmap的容量为2n,不是随意的大小,举个例子:也就是你元素有5个,那你创建的理想数组容量为23=8,而不是5.
扩容的实现:jdk1.8引入了红黑树,比较复杂,此处先不考虑。只考虑jdk1.7的数组+链表的扩容实现:基于新的容量,创建Entry数组,遍历旧的Entry数组,取得数组元素,并释放掉旧数组的对象引用,重新hash计算元素在新数组的位置,将元素放在新数组里。jdk1.7的源码如下:

void transfer(Entry[] newTable) {  
    Entry[] src = table;                   //src引用了旧的Entry数组  
    int newCapacity = newTable.length;  
    for (int j = 0; j < src.length; j++) { //遍历旧的Entry数组  
        Entry<K, V> e = src[j];             //取得旧Entry数组的每个元素  
        if (e != null) {  
            src[j] = null;//释放旧Entry数组的对象引用(for循环后,旧的Entry数组不再引用任何对象)  
            do {  
                Entry<K, V> next = e.next;  
                int i = indexFor(e.hash, newCapacity); //!!重新计算每个元素在数组中的位置  
                e.next = newTable[i]; //标记[1]  
                newTable[i] = e;      //将元素放在数组上  
                e = next;             //访问下一个Entry链上的元素  
            } while (e != null);  
        }  
    }  
}  

5.synchronzied和locak的区别,关于两者选用的标准。

关于区别详解详解synchronized与Lock的区别与使用
关乎选用的一个区别是:lock可设置获取锁的时间,而synchronized没有此项。如果不想线程一直阻塞,可优先考虑lock。

6.数据库索引的底层实现,主键选用什么字段,怎么解决集群的分库分表主键问题

索引的底层实现是:B+树。主键选用的是uuid,解决分库分表时主键重复问题
关于主键选用uuid,面试官持不赞同观点,因为非常影响效率,建议是自己分离出个全局的主键生成模块。结束后我想了下,的确如此:主键实质也是一种索引,是一种主键索引。uuid为了尽量保证唯一,根据算法生成的字符串,在生成索引时,因为大小未知,需要计算,并放入到b+树中,这是比较耗费效率的。而自己写个主键生成模块,模式如:当前时间+机器id+pid等字段。完全可以根据前面的时间做到局部有序,相比uuid,极其节省效率。

猜你喜欢

转载自blog.csdn.net/qq_35033270/article/details/88734093