java多线程面试大纲
2021-12-29 17:41:12 2 举报
AI智能生成
登录查看完整内容
点我主页,涵盖 Java 后面面试所有知识点,本人持续整理,目前已帮助20+人拿到 大厂Offer
作者其他创作
大纲/内容
Synchronized 同步锁实现原理
锁升级
动态编译实现锁消除 / 锁粗化
减小锁粒度
锁优化
12 | 多线程之锁优化(上):深入了解Synchronized同步锁的优化方法
Synchronized
AQS
Lock 锁的实现原理
ReentrantLock 独占锁
ReentrantReadWriteLock 读写锁
没有大规模使用:因为不可重入
StampedLock - 盖章锁
13 | 多线程之锁优化(中):深入了解Lock同步锁的优化方法
Lock同步锁
什么是乐观锁?
CAS
乐观锁的实现原理
CPU提供了总线锁定和缓存锁定两个机制来保证复杂内存操作的原子性
CAS 频繁会导致 总线风暴问题
CPU如何实现原子操作
AtomicInteger AtomicLong 内部使用 Unsafe 的 CAS,不断CAS 会造成 CPU 繁忙
LongAdder:
优化 CAS 乐观锁
14 | 多线程之锁优化(下):使用乐观锁优化并行操作
使用乐观锁优化并行操作
Java 中的多线程之锁优化
Linux 使用 vmstat 命令发现 CPU 上下文切换程序
15 | 多线程调优(上):哪些操作导致了CPU上下文切换?
16 | 多线程调优(下):如何优化多线程上下文切换? - CPU 高
Linux 下 vmstat 命令
上下文切换排查工具:
CPU 上下文切换优化
多线程调优
当前对象
修饰实例方法
类对象
修饰静态方法
基于 monitorenter 和 monitorexit 指令实现
给定对象
代码块
对象锁
类锁
简单地说,synchronized修饰,表现为两种锁:一种是对调用该方法的对象加锁,俗称对象锁或实例锁,另一种是对该类对象加锁,俗称类锁。
三种应用方式:
Java语法规定,任何线程执行同步方法、同步代码块 之前,必须先获取对应的监视器。JAVA的synchronized()方法类似于操作系统概念中的互斥内存块,在JAVA中的Object类型中,都是带有一个内存锁的,在有线程获取该内存锁后,其它线程无法访问该内存,从而实现JAVA中简单的同步、互斥操作。
内存锁
synchronized实现原理synchronized 在 JVM 的实现原理是基于进入和退出管程(Monitor)对象来实现同步。但 synchronized 关键字实现同步代码块和同步方法的细节不一样,代码块同步是使用 monitorenter 和 monitorexit 指令实现的,方法同步通过调用指令读取运行时常量池中方法的 ACC_SYNCHRONIZED 标志来隐式实现的。
public class SynTest{\tpublic int i; public void syncTask(){\t\tsynchronized (this){\t\t\ti++;\t\t}\t}}
反编译后
锁住代码块时的底层原理?(类锁)
public class SynTest{\tpublic int i; public synchronized void syncTask(){\t\ti++;\t}}
jvm 可以从方法常量池中的方法表结构的 ACC_SYNCHRONIZED 的标记区分一个方法是否需要加锁;当方法调用时,调用指令将会检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,执行线程将先持有 monitor, 然后再执行方法,最后在方法完成(无论是正常完成还是非正常完成)时释放 monitor。
锁住方法时的底层原理?(对象锁)
sychronized 的实现原理:
一个 java 对象包含哪些东西?在JVM中,对象在内存中的布局分为三块区域:对象头、实例数据 ,对齐填充。
对象头和 Markword 的关系?对象头中有一个Mark Word 和 Class Metadata Address 组成;
Mark Word 和 Sychronized 的关系?Mark Word 存储了 Sychronized 锁的标注位信息
java 对象头:Java对象头是synchronized实现的关键,synchronized用的锁是存在Java对象头里的。
无锁状态、偏向锁状态、轻量级锁状态和重量级锁状态,这几种状态会随着竞争情况逐渐升级,但不能降级,目的是为了提高锁和释放锁的效率。
Sychronized 锁的四种状态:
非常棒的文章
Synchroized
大白话聊聊Java并发面试问题之微服务注册中心的读写锁优化
读写锁 - ReentrantReadWriteLock
是平常使用得最多的一个方法,就是用来获取锁。如果锁已被其他线程获取,则进行等待。如果采用Lock,必须主动去释放锁,并且在发生异常时,不会自动释放锁。因此一般来说,使用Lock必须在try{ }catch{ }块中进行,并且将释放锁的操作放在finally块中进行,以保证锁一定被被释放,防止死锁的发生。
lock( )方法
方法是有返回值的,它表示用来尝试获取锁,如果获取成功,则返回true,如果获取失败(即锁已被其他线程获取),则返回false,也就说这个方法无论如何都会立即返回。在拿不到锁时不会一直在那等待。
tryLock( ) 方法
方法和tryLock()方法是类似的,只不过区别在于这个方法在拿不到锁时会等待一定的时间,在时间期限之内如果还拿不到锁,就返回false。如果一开始拿到锁或者在等待期间内拿到了锁,则返回true。
lockInterruptibly( ) 方法
unLock( )方法
1.可重入锁
2. 可中断锁
3.公平锁
4.非公平锁
5. 读写锁
与锁相关的几个概念
可重入锁 - ReetrantLock 实现了Lock接口
Lock
表示加锁的状态。
使用CAS 把 state 由 0 -> 1
state变量
表示当前加锁的线程。
加锁线程
阻塞
wait
唤醒
singnal
signalAll
Conditionsynchronized与wait()和nitofy()/notifyAll()方法相结合可以实现等待/通知模型,ReentrantLock同样可以,但是需要借助Condition,且Condition有更好的灵活性,具体体现在:1、一个Lock里面可以创建多个Condition实例,实现多路通知2、notify()方法进行通知时,被通知的线程是Java虚拟机随机选择的,但是ReentrantLock结合Condition可以实现有选择性地通知,这是非常重要的
//第一个条件当屏幕上输出到3\t\tfinal Condition reachThreeCondition = lock.newCondition();\t\t//第二个条件当屏幕上输出到6\t\tfinal Condition reachSixCondition = lock.newCondition();
Condition
数据结构
成功:执行逻辑,执行完,state--,唤醒AQS中队列的head 节点的线程(如果有的话)
是:state++
然后判断自己是不是head,是的话就CAS 尝试获取锁,失败的话就阻塞等待唤醒。
否:构建 Node 节点把自己添加到阻塞队列。(使用CAS的方法)
失败:说明已经有线程获取了锁,判断正在加锁的线程是否是自己
1,线程A调用lock 方法,先CAS 设置 state 变量
AQS lock 操作流程图
AQS图解
AQS 提供了基本的加锁释放锁的方法,还有Condition等功能;ReentrantLock 在内部类中扩展了AQS 的功能,实现了公平锁和非公平锁。
类解释
类继承关系
ReentrantLock内部包含了一个AQS对象,也就是AbstractQueuedSynchronizer类型的对象。这个AQS对象就是ReentrantLock可以实现加锁和释放锁的关键性的核心组件。
AQS 使用了模板方法设计模式使用的时候,需要继承 AbbstractQueuedSychronized 并重写指定的方法;通常是将子类定义为同步组件的静态内部类。
AQS 和 ReentrantLock 的关系?
ReentrantLock 内部基于 AQS 对象 实现了公平锁和非公平锁
font color=\"#f15a23\
大白话聊聊Java并发面试问题之公平锁与非公平锁是啥?
公平锁与非公平锁 (ReentrantLock 构造函数可以选择使用公平锁和非公平锁)
大白话聊聊Java并发面试问题之谈谈你对AQS的理解?
AQS-- AbstractQueuedSynchronizer
有了Synchroized为什么还要Lock?
简述synchronized和java.util.concurrent.locks.Lock的异同 ?
修饰代码块
作用对象
JDK1.6 对锁的实现引入了大量的优化,如偏向锁、轻量级锁、自旋锁、适应性自旋锁、锁消除、锁粗化等技术来减少锁操作的开销。锁主要存在四中状态,依次是:无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态,他们会随着竞争的激烈而逐渐升级。注意锁可以升级不可降级,这种策略是为了提高获得锁和释放锁的效率。
Synchroized 和 Lock 对比
java中有几种锁?
总线风暴问题?
可见性有序性
能!回答这个之前要先说一下 volatile 的作用:内存可见性,禁止指令重排。并没有保证原子性的功能。但是对 两个 java 的基本数据类型 long 和 double 例外,因为 long 类型的变量读取不是原子的,先读取前 32 位,再读取后 32 位。但是如果被 volatile 修饰,那就是可以保证原子操作
volatile 能使得一个非原子操作变成原子操作吗?
图
volatile 修饰符使用实践?volatile 怎么实现内存可见性的?-- 内存屏障
volatile
指两个或两个以上线程在执行中,因争夺资源造成的一种互相等待的现象。
死锁
死锁与活锁的区别?
由于某些条件不满足,导致一直重复尝试,失败,尝试,失败。。。
活锁
抢占式调度+线程优先级
导致线程饥饿的原因?
一个或者多个线程由于无法获取所需资源,导致一直无法执行的状态。
饥饿
死锁,活锁,饥饿 是什么以及它们的区别?
一个线程在访问一个对象的同步方法时,另一个线程可以同时访问这个对象的非同步方法。
一个线程在访问一个对象的同步方法时,另一个线程不能同时访问这个同步方法。
一个线程在访问一个对象的同步方法时,另一个线程不能同时访问这个对象的另一个同步方法。
当一个线程进入一个对象的 sychronized 修饰的实例方法后,其它线程是否还可以进入其它非 sychronized 修饰的方法?
java 中的锁
进程:是操作系统资源分配的最小单元。线程: 是操作系统资源调度的最小单元。一个程序至少有一个进程,一个进程至少有一个线程
进程与线程的区别?
进程间的几种通信方式说一下?
线程间的通信主要用来做线程同步
线程间的通信方式?
继承 Thread 类 --> run()
实现 Runnable 接口 ---> run()
实现 Callable 接口 --> call(),推荐,可以在线程结束时在 call 方法中返回值
Callable 被线程执行后用来产生结果,Future 用来获取结果。
FutureTask
什么是 Callable 和 Future 和 FutureTask?
java 中有几种方法可以实现一个线程?
JAVA的线程生命周期模型在操作系统的基础上稍作了修改,一是Runnable表示可运行与运行状态(等待资源依然是Runnable),二是将休眠状态扩展成了Blocked&Waiting&Time_waiting三种状态。
java的线程与通用线程生命周期?
当是使用 new 创建一个线程后,线程处于新建状态,此时由JVM为其分配内存,并初始化成员变量的值。
初始状态(New)
完事具备只欠CPU
当线程调用了 start() 方法后,就处于 就处于 Runnable 状态,JVM会为其创建方法调用栈和程序计数器,等待调度运行。
就绪状态(Runnable)
处于 Runnable 状态的线程获得CPU,开始执行 run() 方法,该线程处于 Running 状态。Running状态的线程如果调用一个阻塞的 API(例如以阻塞方式读文件)或者等待某个事件(例如条件变量比如 sychronized 锁),那么线程的状态就会转换到休眠状态,同时释放 CPU 使用权,休眠状态的线程永远没有机会获得 CPU 使用权。当等待的事件出现了,线程就会从休眠状态转换到Runnable状态。
运行状态(Running)-java中不存在
当处于 Running 状态的线程失去CPU,变进入 休眠 状态。
休眠状态(Blocked,Waiting,TimedWaiting)
stop() 会强制杀死这个线程,可能导致 Lock 锁不会释放,导致死锁发生,不推荐使用。
终止状态(TERMINATED)
操作系统的五个状态?
极客时间
Thread 的生命周期?
一,使用共享变量 boolean 标记
二,使用 Thread.interrupt() 方法,该方法虽然不会中断一个正在运行的线程, 但是它可以使一个被阻塞的线程抛出一个中断异常,从而使线程提前结束阻塞状态。
如何停止一个正在运行的线程?
运动员起跑程序
构造函数设置计数器起始值,countDown() 计数器减一,当计数器减到0 时,readyLatch.await() 不再阻塞。
CountDownLatch(计时器)
运动员准备起跑程序
各个线程都必须完成某项任务(写数据)才能继续做后续的任务;各个线程需要相互等待,不能独善其身。
适用于以下场景:
CyclicBarrier(循环屏障)
在java 多线程编程中你有那些最佳实践?
免锁容器。好处是当有多个线程同事遍历修改这个 列表时,不会发生 ConcurrentModificationException 异常。
在 CopyOnWriteArrayList 中,写入操作会导致创建整个底层数组的副本,原数组会保留在原地。是的副本数组在被修改时,读取操作可以安全执行。
由于写操作会拷贝数组,消耗内存,如果数组过大,可能会导致频繁 Yong GC 或者 Full GC
不能用于实时读的场景,拷贝数组需要时间,所以调用 set 操作后,读取的值可能还是旧的。虽然 CopyOnWriteArrayList 可以做到最终一致性,但无法保证实时性。
缺点:
读写分离。
使用额外空间的思路,解决并发冲突。
最终一致性。
设计思想:
什么是 CopyOnWriteArrayList?可用于什么场景?
多线程
耗费资源
不能控制线程数量
一些特殊功能比如定时执行的线程,不方便
直接 new Thread 的缺点?
复用线程
控制线程数量
使用简单
Executor 框架的优点?
Executor 和 Executors 的区别?
ForkJoin 框架?
为什么使用 Executor 框架?
任务调度流程图
线程池底层原理?
线程池?
两个方法都可以向线程池中提交任务。
submit() 方法定义在 Executor 接口中,返回类型是 void
execute() 方法定义在 ExecutorService 接口中,返回持有运算结果的 Future 对象。
线程池的 submit() 和 execute() 方法有什么区别?
FixedThreadPool(固定线程数线程池)
CachedThreadPool(可缓存的线程池)
SingleThreadExecutor(单线程池:创建一个核心线程)
DelayQueue 实现
ScheduledThreadPool(固定数量,支持定时,和周期的线程)
实现Runnable接口和Callable接口的区别?
执行execute()方法和submit()方法的区别是什么呢?
ThreadPoolExecutor
流程
四种拒绝策略: RejectedExecutionHandler rejected = null; rejected = new ThreadPoolExecutor.AbortPolicy();//默认,队列满了丢任务抛出异常 rejected = new ThreadPoolExecutor.DiscardPolicy();//队列满了丢任务不异常 rejected = new ThreadPoolExecutor.DiscardOldestPolicy();//将最早进入队列的任务删,之后再尝试加入队列 rejected = new ThreadPoolExecutor.CallerRunsPolicy();//如果添加到线程池失败,那么主线程会自己去执行该任务
四种拒绝策略
基于数组的先进先出
有界
ArrayBlockingQueue
基于链表的先进先出
无界
LinkedBlockQueue
无缓冲的等待队列
SynchronizedQueue
三种阻塞队列
corePoolSize: 线程池维护线程的最少数量maximumPoolSize:线程池维护线程的最大数量keepAliveTime: 线程池维护线程所允许的空闲时间unit: 线程池维护线程所允许的空闲时间的单位workQueue: 线程池所使用的缓冲队列(三种)handler: 线程池对拒绝任务的处理策略(四种)
线程池的 6 个参数四种拒绝策略三种阻塞队列
Java线程池有哪些参数?阻塞队列有几种?拒绝策略有几种?
JDK7提供了7个阻塞队列。分别是Arrayblockingqueue :一个由数组结构组成的有界阻塞队列LinkedBlockingQueue:由链表结构组成的有界阻塞队列PriorityblockingQueue:一个支持优先级排序的无界阻塞队列De layQueue:一个使用优先级队列实现的无界阻塞队列SynchronousQueue:一个不存储元素的阻塞队列。Linkedtransfer@ueue:一个由链表结构组成的无界阻塞队列LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。
/** Main lock guarding all access */ final ReentrantLock lock; /** Condition for waiting takes */ private final Condition notEmpty; /** Condition for waiting puts */ private final Condition notFull; public E take() throws InterruptedException { final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { while (count == 0) notEmpty.await(); return dequeue(); } finally { lock.unlock(); } }
take() 方法没有值的时候会阻塞,阻塞使用的是 Conidtion 的await()方法实现的。
有很多实现类
Block ingQueue 是一个支持两个附加操作的队列接口。* 在队列为空时,获取元素的线程会等待队列有值;* 在队列已满时,存储元素的线程会等待队列可用。
定义的方法说明
Queue
什么是阻塞队列(BlockingQueue)?
美团好文
线程池
Java并发基础面试看我就够了
java并发基础
0 条评论
回复 删除
下一页