发布于 

神策数据一面

09.19 神策数据一面

全程40min,中规中矩,都是些基础问题,没有项目问题。

  1. 自我介绍;

  2. 手撕 + 解释思路:求两个升序数组的交集元素;

  3. 你刚才用到了HashSet和HashMap,他们是线程安全的吗?

  4. 那你ConcurrentHashMap是怎么实现线程安全的呢?

  5. 你知道ConcurrentHashMap的size()方法是怎么实现的吗?

    这里我答的很简单,只是答了没有并发的情况下怎么实现,确实没有具体看size()方法,后来下去查了一下。


    JDK1.8 ConCurrentHashMap的大小 size 通过 baseCount counterCells 两个变量维护:

    • 在没有并发的情况下,使用一个volatile修饰的 baseCount 变量即可;
    • 当有并发时,CAS 修改 baseCount 失败后,会使用 CounterCell 类,即创建一个CounterCell对象,设置其volatile修饰的 value 属性为 1,并将其放在ConterCells数组的随机位置;

    最终在sumCount()方法中通过累加 baseCountCounterCells数组里每个CounterCell的值得出Map中的总大小Size。

    注:然而返回的值是一个估计值;如果有并发插入或者删除操作,和实际的数量可能有所不同。

    另外size()方法的最大值是 Integer 类型的最大值,而 Map 的 size 有可能超过 Integer.MAX_VALUE,所以JAVA8 建议使用 mappingCount()


  6. 说一说多线程里有哪些线程之间同步和调用常用的方法?

  7. 你刚才说到了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竞争。


  8. 你知道ThreadLocal吗?介绍一下底层原理;

  9. 如果现在你的程序或者项目在执行过程中卡住了,你分析一下是什么问题?

    这里我答了两点:

    1. 可能是因为此时正在进行GC,因为GC会导致STW,整个程序都要暂停;
    2. 可能是因为内存分配不合理,造成了内存溢出或CPU飙高。

    然后针对这两点分别提出了排查思路和解决方案。结束后,下来查了一下这个问题,发现可以从很多角度去答。


    除了上述提到的两种情况,还可能有以下情况:

    1. 有可能是网络出现问题;
    2. 有可能是线程池的参数设置不规范,比如线程池太小,核心线程数太少,拒绝策略不对等等;
    3. 有可能是sql导致的,比如慢sql、未建立正确索引导致全表查询、索引失效;

  10. 你知道-Xms-Xmx分别是什么意思吗?如果现在有一台新机器让你来配置这两个参数,你会怎么配置?

  11. Spring里的事务了解吗?介绍一下;

  12. 除了你刚刚说的两种异常情况导致事务失效的情况,其实事务传播特性设置失效也会导致,你了解吗?

    这里我答了事务的几种传播特性。

  13. 你知道事务传播特性实现的原理是什么?

    这我确实不知道,就如实跟面试官说了,面试官说:没事,其实本质就是动态代理。结束后详细查了一下。


    Spring事务是通过代理来实现的,通常是通过CGLIB操作字节码来生成子类,因为要动态加入开启/提交事务的这些代码。


  14. 场景题:现在有10亿条数据,有10台服务器,每台服务器只能存1亿条数据,数据以key-value形式存储,请问我取数据的时候怎么知道我要取的数据在哪个服务器上呢?

    这里我答的是通过hash计算将数据根据hash值存放在对应的服务器上,就像Redis中的分片集群一样,Redis中是分成哈希槽,然后通过hash计算,决定将哪些数据存放在哪个槽里。

  15. 那如何保证数据存放的均匀呢?避免出现一台或几台数据集中存放?

    因为存放采取的就是hash法计算存储的位置,所以可以通过避免hash冲突的方法解决这个问题,比如线性探测法、再hash法或者用HashMap用的链地址法解决冲突,避免数据集中存放的问题。

  16. 如果现在又加了5亿条数据,同时新增了5台服务器,怎么样保证新数据和老数据不会重复?老数据怎么进行数据迁移?

    因为用的就是hash法找位置进行存储,所以可以用HashSet来去重,保证新老数据不会重复。至于老数据的迁移方法就像HashMap的扩容过程一样:

    HashMap在扩容之后也会进行数据迁移,迁移的位置就相当于将原先位置的hash值与旧长度进行“与”操作:

    • 如果结果是0,那么当前元素的桶位置不变;
    • 如果结果为1,那么桶的位置就是原位置 + 原数组长度;

    这样就可以完成老数据的数据迁移。

  17. 反问环节;


本站由 Cccccpg 使用 Stellar 主题创建。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。