欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

京东提前批面经

程序员文章站 2022-04-15 23:33:45
自我介绍说一下final fianlly finaliza的区别将一下你对GC的理解stringbuilder 和stringbuffer的区别redis的数据结构有哪些统计出一个网页中一段时间的用户点击次数hashmap的结构hashmap在多线程环境下可能会产生的问题socket和websocket的区别有一张表有10亿数据 如何迁移到另一张表上mybatis的原理知道吗有三个线程A B C 如何让他们顺序执行问题4 :string 字符串常量stringbuffer 字....
  1. 自我介绍
  2. 说一下final fianlly finaliza的区别
  3. 将一下你对GC的理解
  4. stringbuilder 和stringbuffer的区别
  5. redis的数据结构有哪些
  6. 统计出一个网页中一段时间的用户点击次数 你对sroce是怎么理解的
  7. hashmap的结构
  8. hashmap在多线程环境下可能会产生的问题
  9. socket和websocket的区别
  10. 有一张表有10亿数据 如何迁移到另一张表上
  11. mybatis的原理知道吗
  12. 有三个线程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