Java并发八股学习日记

什么是时间轮,有哪些应用场景

时间轮式一种专门为海量定时任务设计的调度算法,本质就是一个环形数组,每个数组元素叫做槽,槽里挂着一条双向链表存待执行的任务。

和普通任务堆实现的定时器不同,时间轮的任务插入和删除都是O(1)复杂度

时间轮像钟表一样运行。指针每秒走一格,走到那个槽就执行那个槽里的人物。假设时间轮有8格槽,槽的时间单位是1秒,现在要加一个延迟5秒的任务,直接算5%8+1=6,放到第6格槽里。指针转5下就到这个槽,任务就执行了

任务场景

  1. 网络超时管理:Netty里大量用时间轮处理来凝结超时,心跳检测。想想一个网关服务同时有50万个长连接,每个来凝结都有超时判断,用堆根本扛不住
  2. 缓存过期清理:Caffeine这个高性能本地缓存就用时间轮管理key的过期,比逐个扫描高效太多
  3. 消息队列延迟投递:kafka内部的延迟消息就是靠多层时间轮实现的,能处理几天甚至更长的延迟

什么是Java的StampedLock

StampedLock是Java8引入的一种锁机制,相比ReentrantReadWriteLock最大的改进是引入了乐观读锁 。传统的读写锁再读的时候也要加锁,而StampedLock的乐观锁压根不加锁,只是拿到一个时间戳,读完再校验一下这期间有没有什么写操作。如果没有,读就成功了;如果有写操作介入;再降级成悲观读锁重试。

这种设计在读多写少的场景下性能提升非常明显,因为大部分读操作都不用真正竞争锁。

StampedLock提供三种锁模式:

  1. 写锁:独占模式,和ReentrantLock一样,同一时刻只能由一个线程持有写锁
  2. 悲观读锁:共享模式,多个线程可以同时持有读锁,但写锁必须等所有读锁释放
  3. 乐观读锁:不是正真的锁,只是获取一个stamp时间戳,读完后通过validate方法校验期间有没有写操作

什么是Java的ForkJoinPool

ForkJoinPool是Java7引入的一个专门处理分支任务的线程池。核心思想是把一个大人物递归拆成小任务,小任务并行执行完再把结果合并起来

和普通线程池最大的区别是工作窃取算法:每个工作线程都有自己的双端队列,自己产生的子任务从队列头部取,空闲的时候会从别的线程队尾头任务来执行。这样能把CPU资源榨的比较干净,不会出现有的线程忙死,有点线程闲着。

ForkJoin执行流程:

  1. Fork阶段:大人物递归拆分成小任务,每个子任务提交到当前线程的工作队列
  2. Compute阶段:小任务再各个线程并行执行
  3. Join阶段:等待小任务执行完成,把结果合并起来,只曾向上汇总

如何在Java中控制多个线程的执行顺序

控制线程执行顺序的核心思路就一个:让后台执行的线程等着先执行的线程干完活再动

最常用的几种方式:

  1. Thread.join()最直接的方式。在主线程里调用t1.join(),主线程救护堵塞主,等t1跑完才继续往下走
  2. CompleteableFuture的链式调用。thenRun()会在前一个任务完成后自动触发下一个任务
  3. 单线程线程池,把任务按书讯提交进去,天人就是串行执行
java 复制代码
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.submit(() -> System.out.println("T1"));
executor.submit(() -> System.out.println("T2"));
executor.submit(() -> System.out.println("T3"));
executor.shutdown();

join()方式是主线程一次启动并等待每个线程;CompleteableFuture是通过回调来闹自动串联任务;单线程池是队列排队执行。

什么是阻塞队列

阻塞队列的核心是:队列满了会阻塞,队列恐龙取数据会阻塞。主要用在生产者-消费者模式,线程池底层也是靠它来存任务的。

常见的阻塞队列:

  1. ArrayBlockingQueue,基于数组的有界队列。创建时必须指定容量,内部用一把ReentrantLock控制并发,可以选公共锁或非公平锁。
  2. LinkedBlockingQueue,基于链表的队列。可以指定容量变成有界队列,不指定就是无界队列(最大IntegerMAX_VALUE)。内部用了两把锁,put 和 take 分开,并发度比ArrayBlockingQueue高。
  3. PriorityBlockingQueue,无界的优先级队列。元素按优先级出队,不是先进先出。底层是堆结构,放进去的对象要实现Comparable 或者传入Comparator。
  4. DelayQueue,延迟队列。元素必须实现Delayed 接口,只有到期了才能被取出来。缓存过期清理、定时任务调度都能用它。
  5. SynchronousQueue,没有容量的队列。每次 put 必须等有线程take,反之亦然。
    Executors.newCachedThreadPoolO内部就用的它,任务来了直接交给线程,不存队列里。

什么是Java的CAS操作

CAS是一种硬件级别的原子操作,它比较内存中的某个值是否为预期值,如果是就更新为新值,不是就不动。整个比较和交换的过程是一条CPU指令完成的,不可能被打断

CAS需要三个操作:遍历的内存地址,旧的预期值,新值。CPU根据内存地址拿到当前值,和预期值比较,相等就替换成新值,不相等旧返回失败。失败了一般就会自旋重试,直到成功为止。

相关推荐
像我这样帅的人丶你还2 小时前
Java 后端详解(三):全局异常处理与 JPA 数据库映射
java·后端
NE_STOP2 小时前
vibe Coding -- 小项目实战
java
未秃头的程序猿8 小时前
Java 26正式发布!这3个新特性,让代码量直接减半
java·后端·面试
用户298698530148 小时前
Word 文档文本查找与替换的 Java 实现方案
java·后端
阿哉8 小时前
Nacos 服务发现源码:藏在背后的两套事件机制,90%的人只讲了一半
java
咖啡八杯8 小时前
GoF设计模式——命令模式
java·设计模式·架构
AI人工智能_电脑小能手9 小时前
【大白话说Java面试题 第125题】【并发篇】第25题:说说 Java 线程的中断机制
java·后端·面试
Java内核笔记9 小时前
Spring Security 源码解析(六)无状态 JWT 实践:Session 共享与自定义过滤器
java·后端
荣码9 小时前
LangGraph多Agent协作:3个Agent干活比1个强,但我踩了4个坑
java·python
唐青枫10 小时前
Java 虚拟线程实战指南:从 Thread API 到 Spring Boot 高并发应用
java