神策数据一面
09.19 神策数据一面
全程40min,中规中矩,都是些基础问题,没有项目问题。
自我介绍;
手撕 + 解释思路:求两个升序数组的交集元素;
你刚才用到了HashSet和HashMap,他们是线程安全的吗?
那你ConcurrentHashMap是怎么实现线程安全的呢?
你知道ConcurrentHashMap的
size()
方法是怎么实现的吗?这里我答的很简单,只是答了没有并发的情况下怎么实现,确实没有具体看
size()
方法,后来下去查了一下。
JDK1.8 ConCurrentHashMap的大小
size
通过baseCount
和counterCells
两个变量维护:- 在没有并发的情况下,使用一个
volatile
修饰的baseCount
变量即可; - 当有并发时,CAS 修改
baseCount
失败后,会使用CounterCell
类,即创建一个CounterCell
对象,设置其volatile
修饰的value
属性为 1,并将其放在ConterCells
数组的随机位置;
最终在
sumCount()
方法中通过累加baseCount
和CounterCells
数组里每个CounterCell
的值得出Map中的总大小Size。注:然而返回的值是一个估计值;如果有并发插入或者删除操作,和实际的数量可能有所不同。
另外
size()
方法的最大值是 Integer 类型的最大值,而 Map 的 size 有可能超过Integer.MAX_VALUE
,所以JAVA8 建议使用mappingCount()
。
- 在没有并发的情况下,使用一个
说一说多线程里有哪些线程之间同步和调用常用的方法?
你刚才说到了
sleep()
方法,那你觉得sleep(0)
有意义吗?这里我也只是简单的答了一下有意义,因为可以让程序暂时放弃cpu,下来去查了一下更完整的答法。
Thread.Sleep(0)
并非是真的要线程挂起0毫秒,这个操作的意义在于这次调用Thread.Sleep(0)
的当前线程确实的被冻结了一下,让其他线程有机会优先执行。Thread.Sleep(0)
是指当前的线程暂时放弃cpu,也就是释放一些未用的时间片给其他线程或进程使用,就相当于一个让位动作。在线程中,调用
sleep(0)
可以释放cpu时间,让线程马上重新回到就绪队列而非等待队列,sleep(0)释放当前线程所剩余的时间片(如果有剩余的话),这样可以让操作系统切换其他线程来执行,提升效率。在线程没退出之前,线程有三个状态,就绪态,运行态,等待态。
sleep(n)
之所以在n秒内不会参与CPU竞争,是因为,当线程调用sleep(n)
的时候,线程是由运行态转入等待态,线程被放入等待队列中,等待定时器n秒后的中断事件,当到达n秒计时后,线程才重新由等待态转入就绪态,被放入就绪队列中,等待队列中的线程是不参与cpu竞争的,只有就绪队列中的线程才会参与cpu竞争,所谓的cpu调度,就是根据一定的算法(优先级,FIFO等。。。),从就绪队列中选择一个线程来分配cpu时间。而
sleep(0)
之所以马上回去参与cpu竞争,是因为调用sleep(0)
后,因为0的原因,线程直接回到就绪队列,而非进入等待队列,只要进入就绪队列,那么它就参与cpu竞争。
你知道ThreadLocal吗?介绍一下底层原理;
如果现在你的程序或者项目在执行过程中卡住了,你分析一下是什么问题?
这里我答了两点:
- 可能是因为此时正在进行GC,因为GC会导致STW,整个程序都要暂停;
- 可能是因为内存分配不合理,造成了内存溢出或CPU飙高。
然后针对这两点分别提出了排查思路和解决方案。结束后,下来查了一下这个问题,发现可以从很多角度去答。
除了上述提到的两种情况,还可能有以下情况:
- 有可能是网络出现问题;
- 有可能是线程池的参数设置不规范,比如线程池太小,核心线程数太少,拒绝策略不对等等;
- 有可能是sql导致的,比如慢sql、未建立正确索引导致全表查询、索引失效;
你知道
-Xms
和-Xmx
分别是什么意思吗?如果现在有一台新机器让你来配置这两个参数,你会怎么配置?Spring里的事务了解吗?介绍一下;
除了你刚刚说的两种异常情况导致事务失效的情况,其实事务传播特性设置失效也会导致,你了解吗?
这里我答了事务的几种传播特性。
你知道事务传播特性实现的原理是什么?
这我确实不知道,就如实跟面试官说了,面试官说:没事,其实本质就是动态代理。结束后详细查了一下。
Spring事务是通过代理来实现的,通常是通过CGLIB操作字节码来生成子类,因为要动态加入开启/提交事务的这些代码。
场景题:现在有10亿条数据,有10台服务器,每台服务器只能存1亿条数据,数据以
key-value
形式存储,请问我取数据的时候怎么知道我要取的数据在哪个服务器上呢?这里我答的是通过hash计算将数据根据hash值存放在对应的服务器上,就像Redis中的分片集群一样,Redis中是分成哈希槽,然后通过hash计算,决定将哪些数据存放在哪个槽里。
那如何保证数据存放的均匀呢?避免出现一台或几台数据集中存放?
因为存放采取的就是hash法计算存储的位置,所以可以通过避免hash冲突的方法解决这个问题,比如线性探测法、再hash法或者用HashMap用的链地址法解决冲突,避免数据集中存放的问题。
如果现在又加了5亿条数据,同时新增了5台服务器,怎么样保证新数据和老数据不会重复?老数据怎么进行数据迁移?
因为用的就是hash法找位置进行存储,所以可以用HashSet来去重,保证新老数据不会重复。至于老数据的迁移方法就像HashMap的扩容过程一样:
HashMap在扩容之后也会进行数据迁移,迁移的位置就相当于将原先位置的hash值与旧长度进行“与”操作:
- 如果结果是0,那么当前元素的桶位置不变;
- 如果结果为1,那么桶的位置就是原位置 + 原数组长度;
这样就可以完成老数据的数据迁移。
反问环节;