Java并发面经(三)

张开发
2026/5/25 15:59:00 15 分钟阅读
Java并发面经(三)
43.AQS底层原理AQS 是 Java 并发包的底层同步框架它通过一个 state 变量来标识资源的占用状态使用 CAS 对 state 进行原子修改同时依靠一个 CLH双向FIFO队列对抢锁失败的线程进行排队管理队列里等待最久的线程能优先获取资源实现公平锁也支持线程从队列两端操作实现非公平锁的插队并实现线程的阻塞与唤醒以此完成整套线程同步机制。44.Redis持久化Redis 提供了 RDB 和 AOF 两种持久化方式。RDB 是默认方式会按设定的时间间隔把内存里的数据定时生成快照保存到磁盘AOF 则会记录下所有执行过的写命令Redis 重启时按顺序重放这些命令就能恢复数据。两种方式也可以一起开启重启时 Redis 会优先用 AOF 恢复数据因为它的数据完整性更高。45.RDB工作原理Redis 在做 RDB 快照持久化时会 fork 出一个子进程专门负责持久化父进程继续正常处理客户端请求。刚创建子进程时父子进程共享内存数据不会立刻占用双倍内存子进程只读取内存数据并写入磁盘文件不修改数据父进程如果要修改内存系统会通过写时复制COW把要改的内存页复制一份再修改保证子进程读到的是完整快照。等子进程写完新的 RDB 文件就替换掉旧文件子进程退出这样既完成了数据持久化又不影响 Redis 对外提供服务。46.AOF工作原理Redis 的 AOF 持久化就是只记录写操作、不记录读操作的日志文件而且只能往后追加内容不能修改之前的记录。在重写 AOF 时会 fork 出一个子进程子进程根据当前内存数据生成精简的恢复命令并写入临时文件父进程则继续正常处理客户端请求同时把新的写命令缓存起来避免重写过程中数据丢失。子进程完成后通知父进程父进程再把缓存的命令也写入临时文件最后用这个新文件替换掉旧的 AOF 文件后续请求继续追加到新文件。Redis 重启时会从头到尾执行一遍 AOF 日志里的所有写命令从而把数据完整恢复回来。47.事务的四大特性原子性、一致性、隔离性、持久性48.事务的并发问题脏读读取到其他事务未提交的 “脏数据”该数据可能因后续回滚而失效不可重复读同一事务内多次读取同一数据结果因其他事务的提交修改而不一致幻读同一事务内多次执行同一查询结果集的行数因其他事务的插入 / 删除而不一致。、49.数据库的四种隔离级别读未提交就是能读到别的事务还没提交的数据隔离最弱脏读、不可重复读、幻读都会出现读已提交只能读到别人已经提交的数据能避免脏读但同一个事务里多次查结果可能不一样还是会有不可重复读和幻读可重复读是 MySQL InnoDB 默认的级别事务执行期间查到的数据始终一致能避免脏读和不可重复读但依然可能出现幻读串行化最严格事务排队执行所有并发问题都能解决但并发效率很低实际很少用。50.ReentrantLock的实现原理ReentrantLock 是基于 AQS 实现的可重入独占锁内部分为公平锁和非公平锁两种实现。它通过 AQS 中的 state 变量表示锁状态加锁时利用 CAS 尝试将 state 从 0 改为 1成功则获取锁若当前线程已持有锁就将 state 递增实现重入抢锁失败的线程会进入 AQS 的双向队列排队并阻塞。解锁时将 state 递减当 state 减为 0 时表示锁完全释放再唤醒队列中等待的下一个线程。51.Redis的主从复制主从复制就是让主节点master负责写从节点slave负责读主节点会把自己的数据和操作日志不断同步给所有从节点让它们的数据保持一致。这样一来既能分担读请求、提高并发主节点挂了也能让从节点顶上保证高可用而且从节点不会阻塞主节点数据同步也比较简单稳定。52.Redis的哨兵模式哨兵模式就是给 Redis 主从结构配了一群 “自动监督员”。它们一直盯着主节点一旦主节点崩了哨兵会自动在从节点里选举一个新的主节点让系统继续能用不用人手动去改配置保证 Redis 不会因为主节点挂掉就直接瘫痪。53.Redis集群Redis 集群就是把多台 Redis 机器组成一个整体通过16384 个哈希槽把数据分散存在不同主节点上既实现了数据分片扩容又自带主从复制和故障转移能扛更高并发、存更多数据某一个节点挂了只影响一部分数据整个集群还能正常用不用依赖哨兵就能实现高可用。54.一致性哈希算法一致性哈希就是一种分配数据的办法它把所有机器和数据都映射到一个虚拟的圆环上一个数据过来就顺着圆环找最近的机器存起来。它最大的好处是加机器或者减机器的时候只有一小部分数据需要换位置不会像简单取模那样一变动就全部乱掉导致缓存大面积失效。实际用的时候还会加虚拟节点让数据分布更均匀不过 Redis 集群不用这个而是用哈希槽来分片。55.分片哈希槽哈希槽就是 Redis 集群用来分片存数据的方案总共固定16384 个槽集群里有几个主节点就把这些槽尽量平均分下去。存数据时先对 key 算个槽号再根据槽号找到对应的节点读写。它的好处是扩容、缩容或故障转移时只需要迁移一部分槽的数据不用动全部数据既稳定又好管理这也是 Redis 集群正式采用的方案。56.Redis分布锁Redis 分布式锁就是靠它单线程不会打架的特点用一条加锁命令保证只有一个服务能抢到锁避免多个服务同时改数据出问题。为了不把别人的锁误删掉要给自己的锁加个唯一标记删锁时用脚本保证判断和删除一步做完。如果业务执行太久锁过期了就用看门狗自动续期防止还没干完锁就失效。不过主从切换的时候可能会丢锁要是要求特别高的场景可以用 Redlock 红锁或者 ZooKeeper 锁来保证更安全。57.布隆过滤器布隆过滤器就是用来快速判断一个数据肯定不存在的工具。它底层就是个很大的二进制数组存数据时会用多个哈希算法算出几个位置把这些位置标记成 1。查询时再算一遍位置只要有一个位置是 0就说明这个数据一定没有如果全是 1只能说大概率存在有可能误判。它占空间很小、查得又快主要用来解决缓存穿透比如有人查根本不存在的 ID直接在前面就拦住不让请求去查数据库。58.Redis缓存的处理过程请求过来时先去 Redis 里查数据如果能查到就直接返回这叫缓存命中如果查不到就去数据库里读取查到之后再把数据存到 Redis 里下次请求就能直接命中。同时还要注意数据更新时要保证缓存和数据库一致一般是先更新数据库再删除缓存避免脏数据59.缓存穿透缓存穿透就是有人一直查根本不存在的数据缓存里没有数据库里也没有每次请求都会直接穿过缓存去查数据库请求多了数据库就扛不住。一般用布隆过滤器过滤掉不存在的数据或者给不存在的 key 缓存空值来解决。60.缓存击穿缓存击穿就是一个热点 Key 刚好过期了这时候大量并发请求同时过来Redis 里查不到就全都打到数据库上把数据库瞬间压垮。解决办法一般是加互斥锁让一个线程去查库重建缓存或者直接给这种热点 Key 设置永不过期。61.缓存雪崩缓存雪崩就是大批量的缓存同时过期失效或者 Redis 直接挂了导致所有请求一下子全都涌向数据库直接把数据库压垮。解决办法就是给缓存过期时间加上随机值避免同时失效再搭配 Redis 集群、服务降级和限流来保证系统不会崩掉。

更多文章