面试题总结一

hashmap的线程不安全体现在哪里 

HashMap不是线程安全的,HashMap在并发场景下可能存在哪些问题?

  • JDK1.7 中,由于多线程对HashMap进行扩容,调用了HashMap#transfer(),具体原因:某个线程执行过程中,被挂起,其他线程已经完成数据迁移,等CPU资源释放后被挂起的线程重新执行之前的逻辑,数据已经被改变,造成死循环、数据丢失。
  • JDK1.8 中,由于多线程对HashMap进行put操作,调用了HashMap#putVal(),具体原因:假设两个线程A、B都在进行put操作,并且hash函数计算出的插入下标是相同的,当线程A执行完第六行代码后由于时间片耗尽导致被挂起,而线程B得到时间片后在该下标处插入了元素,完成了正常的插入,然后线程A获得时间片,由于之前已经进行了hash碰撞的判断,所有此时不会再进行判断,而是直接进行插入,这就导致了线程B插入的数据被线程A覆盖了,从而线程不安全。

改善:数据丢失、死循环已经在在JDK1.8中已经得到了很好的解决,如果你去阅读1.8的源码会发现找不到HashMap#transfer(),因为JDK1.8直接在HashMap#resize()中完成了数据迁移。

总结:HashMap线程不安全的体现:

  • JDK1.7 HashMap线程不安全体现在:死循环、数据丢失
  • JDK1.8 HashMap线程不安全体现在:数据覆盖

以上来源于:https://segmentfault.com/a/1190000038989240

哪些阻塞队列是有界的 哪些是无界的

有界队列:

SynchronousQueue(不存储元素的阻塞队列):内部任何元素的阻塞队列,任何一次插入操作的元素都要等待相对的删除/读取操作,否则进行插入操作的线程就要一直等待,反之亦然。

ArrayBlockingQueue(不存储元素的阻塞队列):此队列按 FIFO(先进先出)原则对元素进行排序,新元素插入队列的尾部,从头部获得元素,这是一个典型的“有界缓存区”,一旦创建了这样的缓存区,就不能再增加其容量,试图向已满队列中放入元素会导致操作受阻塞;试图从空队列中提取元素将导致类似阻塞。

无界队列:LinkedBlockingQueue(基于单向链表的无界的阻塞队列):尾部插入元素,头部 取出元素;其特点是 FIFO(先进先出),ThreadPoolExecutor 线程池中常用的等待队列。它可以指定容量也可以不指定容量,实际上任何无限容量的队列/栈都是有容量的,这个容量就是Integer.MAX_VALUE,指定了容量后,超过容量的插入会被阻塞

LinkedBlockingDeque(基于双向链表的无界的阻塞队列):是一个由链表结构组成的双向阻塞队列,即可以从队列的两端(头部和尾部)插入和移除元素。双向队列因为多了一个操作队列的入口,在多线程同时入队时,也就减少了一半的竞争,既可以从尾部插入/取出元素,还可以从头部插入元素/取出元素

PriorityBlockingQueue( 是一个按照优先级进行内部元素排序的无限队列。存放在PriorityBlockingQueue 中的元素必须实现 Comparable 接口,这样才能通过实现compareTo()方法进行排序。优先级最高的元素将始终排在队列的头部),LinkedTransferQueue (也是一个无限队列,它除了具有一般队列的操作特性外(先进先出),还具有一个阻塞特性),不会保证优先级一样的元素的排序,也不保证当前队列中除了优先级最高的元素以外的元素,随时处于正确排序的位置。并不保证除了队列头部以外的元素排序一定是正确的

LinkedTransferQueue:是一个无限队列,它除了具有一般队列的操作特性外(先进先出),还具有一个阻塞特性,可以由一对生产者/消费者线程进行操作,当消费者将一个新的元素插入队列后,消费者线程将会一直等待,直到某一个消费者线程将这个元素取走,反之亦然。

 maxmumSize 什么时候会失效

设置的maxMumSize小于核心线程数或者设置的最大数大于Cpu数+1

spring如何解决循环依赖问题

 什么是循环依赖,即2个或以上bean 互相持有对方,最终形成闭环,如A依赖B,B依赖C,C又依赖A。这里不是函数的循环调用(是个死循环,除非有终结条件),是对象相互依赖关系,原型(Prototype)的场景是不支持循环依赖的,通常会走到AbstractBeanFactory类中下面的判断,抛出异常

Spring中循环依赖的场景:构造器的循环依赖(Spring解决不了)  2、setter循环依赖

Spring 为了解决单例的循环依赖问题,使用了 三级缓存 ,递归调用时发现 Bean 还在创建中即为循环依赖

检测循环依赖的过程如下:

  • A 创建过程中需要 B,于是 A 将自己放到三级缓里面 ,去实例化 B
  • B 实例化的时候发现需要 A,于是 B 先查一级缓存,没有,再查二级缓存,还是没有,再查三级缓存,找到了!
    • 然后把三级缓存里面的这个 A 放到二级缓存里面,并删除三级缓存里面的 A
    • B 顺利初始化完毕,将自己放到一级缓存里面(此时B里面的A依然是创建中状态)
  • 然后回来接着创建 A,此时 B 已经创建结束,直接从一级缓存里面拿到 B ,然后完成创建,并将自己放到一级缓存里面
  • 如此一来便解决了循环依赖的问题

一句话:先让最底层对象完成初始化,通过三级缓存与二级缓存提前曝光创建中的 Bean,让其他 Bean 率先完成初始化。

redis使用哪种数据结构可以实现延迟队列 实现思路

Redis的zset、list的特性,我们可以利用Redis来实现一个延迟队列 RedisDelayQueue

具体实现:https://blog.youkuaiyun.com/DFSETHTDFD/article/details/113404550

聚集索引和非聚集索引有什么区别 非聚集索引的叶子结点和非叶子结点存放什么

区别:https://blog.youkuaiyun.com/DFSETHTDFD/article/details/113404574

叶子结点存放指向主键的key,非叶子结点存放查找的key

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值