一面
1、HashMap和Hashtable的区别
HashMap 不是线程安全的,Hashtable是线程安全的; 使用了关键字synchronized进行同步; 在jdk1.5开始 有了CurrentHashMap 来替代Hashtable。 CurrentHashMap 性能比 Hashtable 要好,使用了 锁分段 技术,jdk1.8开始使用CAS算法实现。
HashMap 有个加载因子,默认0.75,当桶被填满3/4时,需要rehash,扩展桶的大小。
如果碰到了 hash冲突, 会通过链表来解决这个问题。
HashMap的底层数据结构 是一个Entry<K,V>对象的数据,数据的下标索引 是通过 hash 值与 length-1 (容器长度-1)进行取模(%)运算 得到的。源码中使用的是 indexFor() 方法进行的 按位与(&)运算,而非取模运算(因为二进制运算效率更高,但是代码可读性 大大降低)。
当HashMap的某个索引的冲突值 达到8个以及以上时,会将链表升级成 红黑树(只关心二叉查找树的查找,不关心红黑树的构造以及左旋和右旋,有点复杂 网址: http://www.cnblogs.com/mfrank/p/9227097.html
红黑树网址:https://www.cnblogs.com/skywang12345/p/3245399.html#a2)。
CurrentHashMap:
锁分段 技术(1.8之前):https://blog.csdn.net/bigtree_3721/article/details/76407543
锁分段技术的segment希望数据均匀的分布在每个段落中,所以与key的hash 取余得到key在entry数组中的位置 不一样,
锁分段技术是将 key的hash值 取最高的4位 与 (数组长度-1) 取余; 如果用低的四位的话,会出现有些分段数据量大,有些分段数据量小,不均匀的情况。
CAS实现(1.8版本):https://blog.csdn.net/u010723709/article/details/48007881
2、HashMap的数据结构,为什么新添加的节点要添加到链表头部?
void addEntry(int hash, K key, V value, int bucketIndex) {
Entry<K,V> e = table[bucketIndex];
table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
if (size++ >= threshold)
resize(2 * table.length);
bsp;
bucketIndex是数组的索引位置,如果此索引位置没有entry对象,就没有链表;数组的该位置只有当前值。
如果此索引位置有entry对象,那么 table[bucketIndex] = new Entry<K,V>(hash, key, value, e); 中的 new Entry 就是新的链表的表头,指向的next 就是老的存在hash冲突的key 对应的值。(这个是单向链表)。
见文章: https://www.cnblogs.com/peizhe123/p/5790252.html
3、ConcurrentHashMap支持高并发的原理,段锁为什么要采用重入锁而不是synchronized?
重入锁等待可中断,可使用公平锁和非公平锁,锁可以绑定多个条件(可以根据condition状态(await, signal) 来唤醒指定的线程)。
4、讲一讲AtomicInteger,为什么要用CAS而不是synchronized?
CAS是一个乐观锁,性能高, 基于unsafe的比较方法,联合volatile 实现线程安全。
5、线程池的工作原理,几个重要参数,然后给了具体几个参数分析线程池会怎么做,最后问阻塞队列的作用是什么?
先使用核心线程处理,满了再放入队列; 队列满了,再创建普通线程,直到达到最大的线程数。此时再进来请求,则根据饱和策略处理(默认直接抛出异常)。
线程池的几个重要参数: 核心线程数,最大线程数,队列长度, keepAlive存活时间(活跃线程超过核心线程时,多余的线程的最大存活时间),线程的异常处理。
阻塞队列的作用: 缓冲,避免创建普通线程 带来系统开销。
参考网址:https://www.cnblogs.com/dongguacai/p/6030187.html
6、JMM如何保证内存可见性,Happens-before
https://blog.csdn.net/bryce123phy/article/details/52260168 todo
volatile关键字:
1、当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量刷新到主内存;
2、当读一个volatile变量时,JMM会把该线程对应的本地内存置为无效。线程接下来将从主内存中读取共享变量;
7、Java运行时数据区域画出来,如果有一个对象有一个域是String s = “abc”;,这个s在哪里,“abc”在哪里
s 在虚拟机栈中,存在String对象abc的地址, abc作为一个对象实例,存储在 Java堆上。
二面
1、项目介绍,把项目的角色和用户的表结构画出来,这个表设计符合几范式?让你来改进这个表设计,你会怎么改?
一般表 需要满足第三范式:第一范式:每列不可拆分;第二范式:有主键;第三范式:数据没有冗余。
2、Java反射熟悉吗?如何访问一个类的私有成员?私有函数呢?
java反射类:Class ct = Class.forName("reflectTest.ReflectTest");
//访问类名
ct.getName();
//访问构造函数
ct.getConstructors();
访问方法
ct.getMethods();
//访问字段(成员变量)
ct.getFields();
访问 一个类的私有成员,私有函数 可以使用
Method[] declaredMethods = ct.getDeclaredMethods();
Field[] declaredFields = ct.getDeclaredFields();
3、为什么会出现死锁?你来写一个死锁,如何改一改这个代码避免死锁?(我把synchronized改成重入锁的tryLock)
死锁产生的原因:
1.因竞争资源发生死锁
2.进程推进顺序不当发生死锁
手写死锁代码: 写一下 可重入锁 以及避免死锁的方式。 todo
4、场景设计题,据说是考拉真实场景:
(1)查询某个用户收藏了哪些商品;
(2)查询某个商品被哪些用户收藏了;
(3)查询某个商品被收藏的次数;
设计了四个表,用户表、商品表、收藏表和商品收藏次数表;
如果一个用户快速点击收藏按钮怎么做?
怎么在高并发下保证性能?
怎么保证数据的一致性?等等各种问题。
最后这个问题, 太笼统,不管了。