1.hashmap的底层结构:
HashMap的底层结构是数组和链表(或者是链表和红黑树的组合,具体取决于Java版本)。
在Java中,HashMap使用了哈希表(Hash Table)来实现,它是一种基于数组的数据结构。HashMap内部维护了一个数组,每个数组元素都是一个链表(或红黑树)的头节点,称为桶(Bucket)。
当向HashMap中插入键值对时,首先会根据键的哈希值计算出在数组中的索引位置,然后将键值对插入到对应桶的链表(或红黑树)中。如果发生哈希碰撞(即多个键的哈希值相同),则会将新的键值对插入到链表(或红黑树)的末尾。
在Java 8之前,HashMap使用了数组和链表的组合来解决哈希碰撞。但是随着链表长度的增加,查询效率会下降,因此在Java 8中引入了链表长度达到一定阈值时将链表转换为红黑树的优化策略。这样可以提高在发生哈希碰撞时的查找效率。
总结起来,HashMap的底层结构是一个数组,数组的每个元素都是一个链表(或红黑树)的头节点,用来存储键值对。当发生哈希碰撞时,通过链表(或红黑树)解决冲突。这种结构可以提供较快的插入、查找和删除操作的性能。
2.Synchronized和 Reentrantlock 的区别?
在Synchronized和ReentrantLock是Java中用于实现线程同步的两种机制。
锁类型:Synchronized是Java内置的关键字,而ReentrantLock是一个类。
可重入性:ReentrantLock可以实现可重入锁,即同一个线程可以多次获取同一把锁。而Synchronized是可重入的,因为它会自动管理锁的获取和释放。
锁的获取方式:Synchronized是隐式的,即在进入同步代码块或方法时自动获取锁,并在退出时自动释放锁。而ReentrantLock是显式的,需要手动调用lock()方法来获取锁,并在使用完毕后调用unlock()方法来释放锁。
锁的灵活性:ReentrantLock相比Synchronized提供了更多的灵活性。它支持公平锁和非公平锁,在创建ReentrantLock对象时可以指定锁的类型。而Synchronized总是非公平的。
等待可中断:ReentrantLock提供了lockInterruptibly()方法,可以使等待锁的线程能够响应中断,而Synchronized无法做到这一点。
条件变量:ReentrantLock提供了Condition接口,可以通过它来实现线程间的协作和通信,而Synchronized没有提供类似的机制。
综上所述,Synchronized是Java中最常用的线程同步机制,简单易用,但灵活性较差。而ReentrantLock则提供了更多的特性和灵活性,但使用起来相对复杂一些。选择使用哪种机制取决于具体的需求和场景。
3、如果支付响应(ack)没有收到如何解决?
如果支付响应(ack)没有收到,可以按照以下步骤进行排查和解决:
检查网络连接:首先需要检查网络连接是否正常。如果网络连接不稳定或者存在中断,可能会导致支付响应无法及时到达客户端。可以通过 ping 命令、traceroute 命令等方式检查网络连接情况。
检查支付接口返回结果:需要检查支付接口返回的结果,确保支付请求已经正确地提交到了支付系统,并且支付系统已经正确地处理了请求。如果支付请求没有被正确处理,可能会导致支付响应无法及时到达客户端。可以通过查看支付接口返回的结果来确认这一点。
检查客户端代码:需要检查客户端代码是否正确地处理了支付响应。如果客户端代码出现了 bug 或者逻辑错误,可能会导致支付响应无法正确地处理。可以通过代码调试、日志记录等方式来排查客户端代码问题。
联系支付系统提供商:如果以上方法都无法解决问题,可以联系支付系统提供商寻求帮助。支付系统提供商可以协助排查问题,并提供相应的解决方案。
4.缓存击穿
是指单个key失效,此时有大量针对这个失效key的并发请求直接进到数据库,导致数据库压力过大,容易宕机。一般是对热点key的访问,解决方案为不设置这个key的过期时间,让它永不过期。
5.缓存穿透
缓存穿透是指对一个不存在的key进行大量并发请求访问,因为不存在,所以直接访问数据库,并且也不会添加到缓存,所以所有请求走的都是数据库,数据库压力过大,容易宕机。解决方案为使用布隆过滤器预先把所有可能存在的值存一遍,用布隆过滤器对用户请求进行过滤,如果key不存在,则直接拒绝,并且由于布隆过滤器底层使用的是bitmap,它是依靠对要存储的值进行多次哈希运算,将得到的值作为位置,将bitmap二进制相应位置置1,所以可以以少量内存存储大量数据。还有一种解决方案为缓存空值。
6.redis主从复制、主从同步流程
redis主从同步分为全量同步和增量同步。全量同步是第一次同步,从节点请求主节点同步数据,主节点检测到是第一次同步数据,然后同步版本信息,把replicationId和offset偏移量给从节点,然后去bgsave生成rdb文件发给从节点读取,生成的途中如果有其他的写操作,则会记录到一个叫repl_baklog的日志文件中,然后把日志文件给从节点同步剩下的。增量同步不是第一次同步,从节点请求主节点同步数据,主节点根据replicationId检测到不是第一次同步数据,然后就获取从节点的offset偏移量,把repl_baklog中自己和从节点的2个offset之间的数据发给从节点进行同步。
7.redis是单线程的,为什么还那么快
首先,redis是基于纯内存操作的。其次,redis单线程,避免了多线程的上下文切换。然后最重要的一点是redis使用了IO多路复用模型,是非阻塞IO。IO多路复用,是指利用一个线程,同时对多个socket进行监测,在某个socket可读或可写时得到通知,而不用遍历所有的socket,充分利用了cpu资源。一般使用epoll模式,它会在通知用户进程socket就绪的同时将socket写入用户空间,不需要遍历socket,提高了效率。redis的网络模型就是采用了IO多路复用和事件的委派机制来应对多个socket请求,它提供了多种事件处理器,包括连接应答处理器,命令请求处理器,命令响应处理器
8.redis分布式锁实现原理
redis分布式锁的实现原理是利用了redis的setnx命令,使用setnx命令设置key时,只有当key不存在时,才能设置成功,所以可以把锁作为key,设置key成功就表示获取了锁,删除key表示释放锁。考虑到可能会因为故障导致一个线程不能释放锁而死锁,需要设置锁的过期时间,然后还需要考虑锁的续期等一系列的问题,redis的框架redisson提供了一个watchdog看门狗的机制来解决这个问题,redisson分布式锁的默认过期时间是30秒,watchdog会不断检测持有锁的线程,每隔十秒,如果业务还没执行完,就会重新续期到30秒。