什么是时间轮,有哪些应用场景
时间轮式一种专门为海量定时任务设计的调度算法,本质就是一个环形数组,每个数组元素叫做槽,槽里挂着一条双向链表存待执行的任务。
和普通任务堆实现的定时器不同,时间轮的任务插入和删除都是O(1)复杂度
时间轮像钟表一样运行。指针每秒走一格,走到那个槽就执行那个槽里的人物。假设时间轮有8格槽,槽的时间单位是1秒,现在要加一个延迟5秒的任务,直接算5%8+1=6,放到第6格槽里。指针转5下就到这个槽,任务就执行了

任务场景:
- 网络超时管理:Netty里大量用时间轮处理来凝结超时,心跳检测。想想一个网关服务同时有50万个长连接,每个来凝结都有超时判断,用堆根本扛不住
- 缓存过期清理:Caffeine这个高性能本地缓存就用时间轮管理key的过期,比逐个扫描高效太多
- 消息队列延迟投递:kafka内部的延迟消息就是靠多层时间轮实现的,能处理几天甚至更长的延迟
什么是Java的StampedLock
StampedLock是Java8引入的一种锁机制,相比ReentrantReadWriteLock最大的改进是引入了乐观读锁 。传统的读写锁再读的时候也要加锁,而StampedLock的乐观锁压根不加锁,只是拿到一个时间戳,读完再校验一下这期间有没有什么写操作。如果没有,读就成功了;如果有写操作介入;再降级成悲观读锁重试。
这种设计在读多写少的场景下性能提升非常明显,因为大部分读操作都不用真正竞争锁。
StampedLock提供三种锁模式:
- 写锁:独占模式,和ReentrantLock一样,同一时刻只能由一个线程持有写锁
- 悲观读锁:共享模式,多个线程可以同时持有读锁,但写锁必须等所有读锁释放
- 乐观读锁:不是正真的锁,只是获取一个stamp时间戳,读完后通过validate方法校验期间有没有写操作

什么是Java的ForkJoinPool
ForkJoinPool是Java7引入的一个专门处理分支任务的线程池。核心思想是把一个大人物递归拆成小任务,小任务并行执行完再把结果合并起来
和普通线程池最大的区别是工作窃取算法:每个工作线程都有自己的双端队列,自己产生的子任务从队列头部取,空闲的时候会从别的线程队尾头任务来执行。这样能把CPU资源榨的比较干净,不会出现有的线程忙死,有点线程闲着。
ForkJoin执行流程:
- Fork阶段:大人物递归拆分成小任务,每个子任务提交到当前线程的工作队列
- Compute阶段:小任务再各个线程并行执行
- Join阶段:等待小任务执行完成,把结果合并起来,只曾向上汇总

如何在Java中控制多个线程的执行顺序
控制线程执行顺序的核心思路就一个:让后台执行的线程等着先执行的线程干完活再动
最常用的几种方式:
- Thread.join()最直接的方式。在主线程里调用t1.join(),主线程救护堵塞主,等t1跑完才继续往下走
- CompleteableFuture的链式调用。thenRun()会在前一个任务完成后自动触发下一个任务
- 单线程线程池,把任务按书讯提交进去,天人就是串行执行
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是通过回调来闹自动串联任务;单线程池是队列排队执行。

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

什么是Java的CAS操作
CAS是一种硬件级别的原子操作,它比较内存中的某个值是否为预期值,如果是就更新为新值,不是就不动。整个比较和交换的过程是一条CPU指令完成的,不可能被打断
CAS需要三个操作:遍历的内存地址,旧的预期值,新值。CPU根据内存地址拿到当前值,和预期值比较,相等就替换成新值,不相等旧返回失败。失败了一般就会自旋重试,直到成功为止。
