有位同学写了一个滴滴面试拿offer的经历,据说还面了滴滴的CTO,我就好奇,这CTO面又能是个啥水平呢?对他在文章中提到的部分问题做个解答吧。
原文请见滴滴CTO五轮面试真是太刺激了,Java高级工程师一二三四五面面经(已拿到offer)_a3961401的博客-CSDN博客blog.csdn.net
Q1. Java线程的状态及转换java线程状态转换图,好老了
New Thread:线程新创建的状态
Thread myThread = new MyThreadClass();
Runnable: 可运行状态
Thread myThread = new MyThreadClass();
myThread.start();
start方法创建运行线程所需要的系统资源.
Not Runnable: 不可运行状态
下面几种情况可以让线程进入不可运行状态Someone invokes its sleep method.
Someone invokes its suspend method.
The thread uses its wait method to wait on a condition variable.
The thread is blocking on I/O.
Dead:死亡状态A thread can die in two ways: either from natural causes, or by being killed (stopped). A thread dies naturally when its run method exits normally. For example, the while loop in this method is a finite loop--it will iterate 100 times and then exit
Q2. Java创建线程的方式
Java有三种创建线程的方式:通过实现Runnable接口
extending Thread类
通过Callable和Future创建线程
3. 创建线程池的方式有哪些
主要有下面四种方法创建线程池newCachedThreadPool: 创建一个可缓存的线程池,如果线程池长度超过处理需求,可灵活回收空闲线程,若无可回收,则新建线程
newFixedThreadPool: 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待
newScheduledThreadPool :创建一个定长线程池,支持定时及周期性任务执行
newSingleThreadExecutor: 创建一个单线程化的线程池,它只会唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO,LIFO,优先级)执行
Q3. JUC包下的类,能说多少说多少
JUC指的是关于java.util.concurrent类包下的常用类。JUC类关系图
这个package下主要的类有如下:Executor
ExecutorService:进行异步处理,管理内存队列和调度提交的任务
ScheduledExecutorService:跟ExecutorService类似,但是提供周期性任务
Future
CountDownLatch:JDK5中引入的工具类,阻塞一组线程、直到某些操作完成
CyclicBarrier
Semaphore
ThreadFactory
BlockingQueue
DelayQueue
Locks
Phaser
Q4. ReentranLock与sychronized的区别
具体参考这篇文章Synchronize和ReentrantLock区别juejin.im
Q5. volitile的内存语义,底层如何实现
对于内存语义,简单的说就是要保证读后写、写后读以及写后写等几种情况的数据一致性。主要方法就是每次写内存时都从线程的共享内存写到主机内存,每次读之前都从主机内存读到线程的共享内存。跟SMP的思想非常类似。
简单的说,底层通过memory barrier来实现不同线程之间的volitile读写。其实了解Linux Kernel变成的同学对这个概念应该并不陌生。Volitile关键字禁止了编译器的代码优化,使特定处理器的编译器针对并发的代码重排被禁止,这种禁止会影响系统的并发性能,但是能保证数据的一致性。也是一种权衡的设计吧。
至于Happend-before原则,可进一步参考这里Volatile的内存语义www.jianshu.com
Q6. ConcurrentMap源码如何实现
并发Map主要采用了分段技术(一种内部数据结构)来获取针对map的并发访问,原理很简单,把map在内部分成很多段,只有访问对应段的数据时,需要对其加锁。其他时候,多个线程可以并发的访问自己的数据部分。极大的改善了并发性能。
默认是提供32个分段
对于每个键值对,则是存储在一个叫做Map.Entry的结构中,见下图
从这个结构里面有,key-value,hash和next。比较好理解,就不多解释了。
ConcurrentMap定义了一组Map.Entry结构来存储键值对
protected transient Entry[] table;
Q7. Zookeeper用过吗,介绍一下https://zhuanlan.zhihu.com/p/157317221zhuanlan.zhihu.com
请参考上面这篇文档
Q8. LSM-tree应用在那些场景、优缺点
在YCombinator上看到了一篇讲解的文章,推一下Introduction to LSM Trees: May the Logs Be with Younews.ycombinator.com
还有另一篇文章https://medium.com/databasss/on-disk-io-part-3-lsm-trees-8b2da218496fmedium.com
LSM Tree全称是Log Structured Merge Tree,也就是日志结构的归并树。我们知道,最简单的数据库实现就是日志化的数据库实现,只在最后做append操作,不考虑查找和in-place的修改。
一个LSM Tree一般由两层或多层的树形数据结构组成,树形结构一般两种形态,C0和C1。
C0被叫做内存表,是完全驻留在内存中的。一般由平衡二叉树AVL实现,将排好序列K-V对都存储在内存中,插入和删除也在这一层完成。
C1一般体积会大很多,所以都存储在磁盘中。C1中包含很多不可修改的日志分段以及对应的hash索引。C1一般使用SSTable实现。
那么SSTables又是个啥?它跟LSM Tree又是如何结合的呢?
我们知道,日志型数据库是为了适应大规模的并发写入场景,这种设计让写入的效率非常高。但是随着写入的日志增大,怎么样更好的管理写入的文件呢?自然的,我们想到了分段(segment)。这就是SSTables派上用场的地方了,它的全名是“String Sorted Tables”,也就是"排序字符串表"。
SSTables主要作用是在不可修改的日志分段中、按照字符串排序顺序来存储K-V对。
它有两个优点:在全数据hash索引不存在的情况下,查找变得非常高效。
Range查询变得可能。Elastic Search中Range String Query时,就用了SSTables。
当内存中的C0结构大小超过阈值时,它就被刷入到磁盘上,并按照SSTable的形态作为一个新段存储起来。
向LSM Tree中插入数据:当写入发生时,数据首先被写入到内存中的memtable中.
当memtable大小超过阈值时,数据被刷写到磁盘.
因为memtable已经有序,创建新的SSTable分段非常高效.
旧的分段会周期性的合并以减少磁盘占用,同时减少分段数据的数量。
从LSM Tree中读入数据:首先在memtable中查找key.
如果没有找到,然后使用hash索引在一个或者多个分段中查找.