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根据内存地址拿到当前值,和预期值比较,相等就替换成新值,不相等旧返回失败。失败了一般就会自旋重试,直到成功为止。

相关推荐
xifangge20251 小时前
【深度排障】从 OS 底层寻址剖析 javac 不是内部或外部命令 核心报错:变量空间隔离与自动化部署终极范式
java·开发语言·jdk·自动化
肖恩想要年薪百万1 小时前
JSP中常用JSTL标签
java·开发语言·状态模式
l1t1 小时前
在aarch64机器上安装clang来生成codonjit python模块
开发语言·python
程序员清风1 小时前
AI开发岗该如何准备面试?
java·后端·面试
笨拙的老猴子2 小时前
Spring AI 实战教程(七):Agent 智能体 —— 用电商购物助手学透自主规划与工具执行
java·人工智能·spring
@codercjw2 小时前
工程图制图经验
学习
谙弆悕博士2 小时前
快速学C语言——第19章:C语言常用开发库
c语言·开发语言·算法·业界资讯·常用函数
月落归舟2 小时前
深入解析Java基础之基础
java·开发语言
折哥的程序人生 · 物流技术专研2 小时前
《Java 100 天进阶之路》第20篇:Java初始化、构造器、对象创建的过程
java·开发语言·后端·面试