顺丰面试题

1、什么是原子操作?

原子操作是指不会被线程调度机制打断的操作。这种操作一旦开始,就一直运行到结束,中间不会有线程切换。 

2、常用的开源类库有哪些?常用哪些类?碰到过哪些坑?fastjson的坑有碰到过吗?

有fastjson、guava、commons-io、commons-lang3、commons-collection4、commons-math3、commons-codec、commons-dbcp2、jedis、es、phoenix、curator-framework。

guava可以用来处理集合(com.google.common.collect包中的类)、缓存(com.google.common.cache包中的类)、原始类型(com.google.common.primitives包中的类)、并发(com.google.common.util.concurrent包中的类)、字符串(com.google.common.base包中的类)、io(com.google.common.io包中的类)、数字计算(com.google.common.math包中的类),比较好用的类有

Multimap接口,全类名是com.google.common.collect.Multimap。子接口有ListMultimap、SetMultimap、SortedSetMultimap、FilteredMultimap、FilteredSetMultimap。ListMultimap接口常用实现类有ArrayListMultimap、LinkedListMultimap,SetMultiMap常用实现类有HashMultimap。Multimaps类里面提供了大量的静态方法来操作Multimap实例。

8个原始类型在guava中都有对应的包装类型,如Bytes、Shorts、Ints、Longs、Floats、Doubles、Chars、Booleans,都在在原始类型名字上加一个s,都在com.google.common.primitives包中。每个类都有很多实用的静态方法,如Ints类的

int[] toArray(Collection collection),这个方法返回的是int数组,而不像Collection实例的toArray()方法一样返回Object数组。内部实现是先调用collection.toArray()方法生成一个Object[],然后再把Object[]中数据一个个放到一个新的int[]中。

sortDescending(int[] array),倒序某数组。内部实现是先调用java.util.Arrays类的sort(int[] array)静态方法(利用双中心快排算法给数组排序,时间复杂度是O(n log(n)),比传统的单中心快排算法要快),然后再反转。

reverse(int[] array),反转数组

int max(int[] array)、int min(int[] array)、String join(String separator, int[] array)、boolean contains(int[] array, int target)

int[] contat(int[]... arrays),拼接多个数组成一个数组

CharMatcher、Joiner、Splitter,都在com.google.common.base包中,配合commons-lang3中的StringUtils使用,效果牛逼。

字符串分隔不要再用String实例的split()方法,因为假如分隔符是转义字符的话,需要对分隔符转义,否则分隔出的结果是不正确的。转义字符那么多,根本记不住。要是IDE有提示功能还好,要是没有提示功能,定位问题能定位到死。StringUtils、Splitter不用考虑转义字符的问题。

在某些情况下,StringUtils的分隔操作也有问题。如StringUtils.split("a,b,,c", ",,")返回的数组中有3个元素,分别是"a"、"b"、"c",这不符合预期,理论上结果数组只应该有"a,b"和"c"两个元素。用Splitter.on(",,").splitToList("a,b,,c")返回的结果中元素只有2个,分别是"a,b"和"c",符合预期。

BaseEncoding,在com.google.common.io包中,用于编解码,在另一篇文章分析。

Cache。guava cache是单机缓存,非分布式缓存。在另一篇文档详细解析。

3、redis key负载均衡策略是什么?集群怎么搭建?持久化是作用在master节点上还是slave节点上?

负载均衡策略是根据key的hash算法,找到对应的slot。

redis cluster节点最少是6个,3主3从。和mysql的主从不同,redis cluster主节点既负责客户端的读,又负责客户端的写,从节点只作为故障转移使用,没有客户端的读写。redis cluster有一个槽(slot)的概念,所有的key根据hash函数映射到16384(214)个槽中,每个节点负责一部分槽和槽内的键值数据。可以动态调整槽的分布,新增节点时槽分布也会自动调整。

4、nginx负载均衡策略有哪些?

 

5、线程池,在什么情况下,任务会放到队列中?

 

6、lambda表达式常用的方法或者说是函数有哪些?哪些是终止函数?

forEach()、forEachOrdered()、map()、flatMap()、peek()、filter()、collect()、reduce()、findAny()、findFrist()、allMatch()、anyMatch()、noneMatch()、distinct()、count()、limit()、skip()、max()、min()、sorted()、unordered()、toArray()、iterator()、isParallel()、parallel()、sequential()

所谓的终止函数指的是返回值不是Stream类型的函数,有:void forEach()、void forEachOrdered()、collect()、reduce()、findAny、findFirst()、allMatch()、anyMatch()、noneMatch()、max()、min()、toArray()、iterator()、isParallel()。

 

7、parallelStream原理。

parallelStream是并行流,依赖jdk1.7出现的Fork/Join框架。

Fork/Join框架的核心是工作窃取(work-stealing)算法。工作窃取算法是指某个线程从其他队列里窃取任务来执行。

那么为什么需要使用工作窃取算法呢?

假如我们需要做一个比较大的任务,我们可以把这个任务分割为若干互不依赖的子任务,为了减少线程间的竞争,于是把这些子任务分别放到不同的队列里,并为每个队列创建一个单独的线程来执行队列里的任务,线程和队列一一对应,比如A线程负责处理A队列里的任务。但是有的线程会先把自己队列里的任务干完,而其他线程对应的队列里还有任务等待处理。干完活的线程与其等着,不如去帮其他线程干活,于是它就去其他线程的队列里窃取一个任务来执行。而在这时它们会访问同一个队列,所以为了减少窃取任务线程和被窃取任务线程之间的竞争,通常会使用双端队列,被窃取任务线程永远从双端队列的头部拿任务执行,而窃取任务的线程永远从双端队列的尾部拿任务执行。

ForkJoinPool是juc包中的类,同ThreadPoolExecutor一样,也继承了AbstractExecutorService。ForkJoinPool的每个工作线程都维护着一个工作队列,这是一个双端队列Deque,里面存放着任务ForkJoinTask。每个工作线程在运行过程中,产生的新任务会放到工作队列的队尾。工作线程在处理自己工作队列任务时,每次是从队尾取任务。当自己的工作队列清空后,会尝试去窃取其他工作队列的任务,且是从队首窃取。

观察ForkJoinTask类的fork()方法:

 

会在当前线程之外,另起几个线程 

8、kafka如何保证高可用?

 

你可能感兴趣的:(顺丰面试题)