最近参加了一下头条后端工程师的面试, 很惨, 一面就挂掉了.java
回来以后也对面试过程作了一些总结,就不夹带私货了,这篇文章主要对面试过程当中的技术问题作一个复盘.node
这是一道 LeetCode 原题, 原题连接面试
求二叉树的最远距离节点间的节点. 首先总结几个规律:redis
因此咱们要作:后端
代码以下:安全
public int diameterOfBinaryTree(TreeNode root) {
AtomicReference<Integer> ret = new AtomicReference<>(0);
find(root, ret);
return ret.get();
}
private int find(TreeNode node, AtomicReference<Integer> result) {
if (node == null) return 0;
int left = 0, right = 0;
if (node.left != null) left = find(node.left,result) + 1;
if (node.right != null) right = find(node.right,result) + 1;
int tmp = Math.max(result.get(), left + right);
result.set(tmp);
return Math.max(left, right);
}
复制代码
代码很简单, 就是递归的求节点的左子树最远叶子和右子树最远叶子. 而后在 计算过程当中, 将 当前节点的直径
做为一个备选项存储,最后求最大直径便可.服务器
Java在1.5添加了自动装箱和拆箱机制. 总的来讲基本就是基本类型和对应的包装类型之间的自动转换.微信
以下面的代码中:并发
public class BoxTest {
public static void main(String [] args){
Integer a = 10; // 装箱
int b = a; // 拆箱
}
}
复制代码
咱们将代码编译以后进行反编译, 能够看到函数
很明显在 代码中的 #2
,#3
处进行了装箱和拆箱.
分别调用了Integer的 valueOf
方法和intValue
方法.
这里不对全部的垃圾收集器展开讲解, 有兴趣的朋友们能够移步 JVM的数据区域与垃圾收集.
众所周知, CMS的垃圾收集过程以下:
因此在初始标记和从新标记两个阶段仍是须要暂停用户线程的.
查看ConcurrentHashMap
的源码能够发现, Node节点的定义是:
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
volatile V val;
volatile Node<K,V> next;
}
复制代码
能够看到, 里面定义了几个属性, 分别以下:
在get(Ojbect)
方法的调用过程当中.
public V get(Object key) {
Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;
//获取hash值
int h = spread(key.hashCode());
//经过tabat获取hash桶, tabAt是一个线程安全的操做, 有UnSafe来保证的.
if ((tab = table) != null && (n = tab.length) > 0 &&
(e = tabAt(tab, (n - 1) & h)) != null) {
//若是该hash桶的第一个节点就是查找结果,则返回
if ((eh = e.hash) == h) {
if ((ek = e.key) == key || (ek != null && key.equals(ek)))
return e.val;
}
//第一个节点是树的根节点,按照树的方式进行遍历查找
else if (eh < 0)
return (p = e.find(h, key)) != null ? p.val : null;
//第一个节点是链表的根节点,按照链表的方式遍历查找
while ((e = e.next) != null) {
if (e.hash == h &&
((ek = e.key) == key || (ek != null && key.equals(ek))))
return e.val;
}
}
return null;
}
复制代码
在这个过程当中,
tabAt
来操做, 线程安全.因此 get
过程当中不用加锁也能够正确的获取对象.
这个问题比较宽泛,我我的的理解有如下两点.
hashmap的rehash过程想必你们都是了解的, 那么这么稍微说一下redis的渐进式hash.
首先, rehash是要将原来表中的全部数据从新hash一遍,存放到新的表格中, 以进行扩容.
而redis是一个单线程的高性能的服务, 若是一个hash表中有几亿条数据, rehash 花费的时间将比较长, 而在此期间, redis是没法对外提供服务的, 这是不可接受的.
所以, redis实现了渐进式hash. 过程以下:
rehashindex
位置上的值rehash到ht[1]上. 将 rehashindex 递增一位.在上面的过程当中有两个问题没有提到:
解决办法是: 在redis的定时函数里, 也加入帮助rehash的操做, 这样子若是服务器空闲, 就会比较快的完成rehash.
解决办法: 对于添加操做, 直接添加到ht[1]上, 所以这样才能保证ht[0]的数量只会减小不会增长,才能保证rehash过程能够完结. 而删除,修改, 查询等操做会在ht[0]上进行, 若是得不到结果, 会去ht[1]再执行一遍.
渐进式hash带来的好处是显而易见的, 他采用了分而治之的思想, 将rehash操做分散到每个对该哈希表的操做上,避免了集中式rehash带来的性能压力.
与此同时,渐进式hash也带来了一个问题, 那就是 在rehash的时间内, 须要保存两个 hash表, 对内存的占用稍大, 并且若是在redis服务器原本内存满了的时候, 忽然进行rehash会形成大量的key被抛弃.
《Redis设计与实现(第二版》
完。
以上皆为我的所思所得,若有错误欢迎评论区指正。
欢迎转载,烦请署名并保留原文连接。
联系邮箱:huyanshi2580@gmail.com
更多学习笔记见我的博客或关注微信公众号 < 呼延十 > ------>呼延十