juc相关
异步编排使用场景以及相关的API?(高频)
将多个独立的异步任务按一定的顺序/条件组合起来
- 并行执行多个无依赖的任务。将串行(A+B+C)优化为max(A,B,C)大幅提升响应速度
- 串行执行有依赖的异步任务。保证任务执行顺序,避免同步阻塞
- 任务执行结果聚合/兜底,比如多个第三方接口获取数据,只要有一个返回就用
异步编排的核心工具是CompletableFuture替代了传统的Future
核心APIsupplyAsync()/allOf()/thenComposeAsync()/exceptionally()/orTimeout();
然后默认的线程池是ForkJoin必须自定义线程池,比做异常处理,使用后要shutdown()
死锁产生的原因
发生死锁必须同时满足四个条件:
- 互斥:资源同一时刻只能被一个线程占用。
- 持有并等待:线程拿着一个锁的同时,还想要另一个锁
- 不可剥夺:线程拿到的锁不能被强行抢走
- 循环等待:线程之间形成环形的锁依赖链
打破任意一个条件就能避免死锁
JMM的理解(是什么,为什么,怎么用)
JAVA内存模型,是用来描述多线程之间内存访问规则和行为的规范,它定义了线程如何与内存交互,如何保证多线程环境下的可见性,有序性,和原子性,硬件和编译器优化等问题
- 可见性:一个线程对变量的修改能及时被其他的线程看到,volatile关键词就是用来保证可见性的,强制线程每次读写操作都直接和主内存交互
- 有序性:线程执行的顺序。JMM允许指令重排序来提高性能,但通过happens-before关系保证跨线程的有序性
- 原子性:操作不可分割。通过synchronized关键字
Happens-Before:如果操作 Ahappens-beforeB,那么 A 的执行结果对 B 可见,且 A 的执行顺序在 B 之前
内存模型:主内存存放共享变量,所有线程都能访问,每个线程都有自己的本地内存,存放共享变量的副本,线程对变量的操作必须在本地内存进行,不能直接操作主内存名单时先城建变量传递必须通过主内存。
怎么创建线程,又怎么结束线程,进程和线程的区别,线程的状态,状态转换的api?
- 继承Thread类,重写run()现在几乎不用
- 实现Runnable接口,实现run()方法,将Runnable对象作为参数传给Thread构造函数,避免单继承
- 使用Callable和FutureTask,与上个区别是能返回结果,也能抛异常
- 使用线程池ExecutorService
- 使用CompletableFuture,java8以后引入的,默认是ForkJoinPool
线程之间是如何通信?
核心手段分两类:共享内存和消息传递
共享内存是最直接的方式,volatile能保证可见性,synchonized和lock能同时解决可见性,原子性,有序性
消息传递则是通过wait/notify,Condition,BlockingQueue
volatile关键词的作用是什么
volatile的核心作用:
可见性:(volatile变量在读取时强制从主内存读取最新值,写入时立刻刷新主内存)
禁止重排序:(在写volatile变量前插入StoreStore屏障,保证前面的写操作先于当前写操作完成;在读volatile变量后插入LoadLoad屏障,保证后面的读操作不会被重排序到当前读之前,从而维持程序的有序性)
你是用过java的累加器吗
java8引入LongAdder和DoubleAdder用来解决高并发场景下Atomiclong性能不够的问题
AtomicLong所有的线程都在枪一个变量,并发上来就疯狂自旋
而LongAdder的思路时把热点分散,内部维护一个base和多个cell,最后求和的时候把base和所有的cell值加起来,扩容上限=cpu核心数
如果想做乘法、取最大值这种自定义操作,可以用 LongAccumulator
:sleep() 和 wait() 的区别?
| 对比 | sleep() |
wait() |
|---|---|---|
| 所属 | Thread 类的静态方法 | Object 类的实例方法 |
| 锁释放 | 不释放锁 | 释放锁 |
| 唤醒 | 时间到 或 interrupt() | notify() / notifyAll() |
| 使用前提 | 任意地方 | 必须在 synchronized 代码块内 |
线程的状态

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐
所有评论(0)