京东提前批面经
- 自我介绍
- 说一下final fianlly finaliza的区别
- 将一下你对GC的理解
- stringbuilder 和stringbuffer的区别
- redis的数据结构有哪些
- 统计出一个网页中一段时间的用户点击次数 你对sroce是怎么理解的
- hashmap的结构
- hashmap在多线程环境下可能会产生的问题
- socket和websocket的区别
- 有一张表有10亿数据 如何迁移到另一张表上
- mybatis的原理知道吗
- 有三个线程A B C 如何让他们顺序执行
问题2:
final 用于声明属性,方法和类, 分别表示属性不可变, 方法不可覆盖, 类不可继承.
finally 是异常处理语句结构的一部分,表示总是执行
finalize 是Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,可以覆盖此方法提供垃圾收集时的其他资源回收,例如关闭文件等. JVM不保证此方法总被调用.
问题4 :
string 字符串常量
stringbuffer 字符串变量 线程安全。append方法上加了synchronized关键字保证一个时间段内只能有一个线程执行该方法
stringbuilder 字符串变量 线程不安全
问题8:
hashmap是线程不安全的
在多线程环境下 可能因为线程竞争 在put()时 因为多线程 某一线程先put的值被覆盖
比如有两个线程A和B,首先A希望插入一个key-value对到HashMap中,首先计算记录所要落到的桶的索引坐标,然后获取到该桶里面的链表头结点,此时线程A的时间片用完了,而此时线程B被调度得以执行,和线程A一样执行,只不过线程B成功将记录插到了桶里面,假设线程A插入的记录计算出来的桶索引和线程B要插入的记录计算出来的桶索引是一样的,那么当线程B成功插入之后,线程A再次被调度运行时,它依然持有过期的链表头但是它对此一无所知,以至于它认为它应该这样做,如此一来就覆盖了线程B插入的记录,这样线程B插入的记录就凭空消失了,造成了数据不一致的行为。
resize()方法 也会有明显的多线程问题 resize()是说创建一个容量是当前桶两倍的桶数组。先将原先的桶的元素逐个移到新桶中,然后将原来的引用置为0 。hashmap的get操作可能因为resize()引起死循环。(cpu100%)
void transfer(Entry[] newTable, boolean rehash) {
int newCapacity = newTable.length;
for (Entry<K,V> e : table) {
while(null != e) {
Entry<K,V> next = e.next;
if (rehash) {
e.hash = null == e.key ? 0 : hash(e.key);
}
int i = indexFor(e.hash, newCapacity);
e.next = newTable[i];
newTable[i] = e;
e = next;
}
}
}
这个方法的功能是将原来的记录重新计算在新桶的位置,然后迁移过去。
我们假设有两个线程同时需要执行resize操作,我们原来的桶数量为2,记录数为3,需要resize桶到4,原来的记录分别为:[3,A],[7,B],[5,C],在原来的map里面,我们发现这三个entry都落到了第二个桶里面。
假设线程thread1执行到了transfer方法的Entry next = e.next这一句,然后时间片用完了,此时的e = [3,A], next = [7,B]。线程thread2被调度执行并且顺利完成了resize操作,需要注意的是,此时的[7,B]的next为[3,A]。此时线程thread1重新被调度运行,此时的thread1持有的引用是已经被thread2 resize之后的结果。线程thread1首先将[3,A]迁移到新的数组上,然后再处理[7,B],而[7,B]被链接到了[3,A]的后面,处理完[7,B]之后,就需要处理[7,B]的next了啊,而通过thread2的resize之后,[7,B]的next变为了[3,A],此时,[3,A]和[7,B]形成了环形链表,在get的时候,如果get的key的桶索引和[3,A]和[7,B]一样,那么就会陷入死循环。
本文地址:https://blog.csdn.net/cool_and_gentle/article/details/107344674