本文主要是介绍Sychronized和ReentrantLock锁 面试题,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
Sychronized和ReentrantLock锁 面试题
- 前言
- 1、Java死锁如何避免?
- 2、公平锁和⾮公平锁的底层实现?
- 3、ReentrantLock中tryLock()和lock()⽅法的区别?
- 4、Sychronized的偏向锁、轻量级锁、重量级锁?
- 5、谈谈你对AQS的理解,AQS如何实现可重⼊锁?
- 6、AQS锁的类别?
- 7、CountDownLatch和Semaphore的区别和底层原理?
- 8、ReentrantLock底层原理?
- 9、Sychronized和ReentrantLock的区别?
- 10、Synchronized 偏向锁有个开关,如果默认开启有什么缺点?
- 11、CAS与传统synchronized区别
- 12、CAS是不是操作系统执行的?
- 13、说一下CAS怎么用的,会有哪些问题?
- 14、cas自增 和 Synchronized 自增 谁快?
- 15、Synchronized 和 ReentracLock 哪个快,为啥?
- 16、怎么检测一个线程是否拥有锁?
- 17、事务未提交而锁提前释放了?
- 18、AtomicInteger计数器自增到一万以后,怎么归零?
- GIT
- 19、什么是git?
- 20、列举工作中常用的git命令:
- 总结
前言
最新的 Java 面试题,技术栈涉及 Java 基础、集合、多线程、Mysql、分布式、Spring全家桶、MyBatis、Dubbo、缓存、消息队列、Linux…等等,会持续更新。
如果对老铁有帮助,帮忙免费点个赞,谢谢你的发财手!
1、Java死锁如何避免?
造成死锁的4个必要条件:
- 1.⼀个线程每次只能占有⼀个资源;
- 2.⼀个线程在阻塞等待某个资源时,不释放已占有资源;
- 3.⼀个线程已占有的资源,在未使⽤完之前,不能被强⾏剥夺;
- 4.多个线程形成循环等待的关系。
如果要避免死锁,只需要其中某⼀个条件不满⾜即可。
在开发过程中: - 1.要注意加锁顺序,保证每个线程按同样的顺序进⾏加锁解锁;
- 2.要注意加锁时限,可以针对锁设置⼀个超时时间;
- 3.要注意死锁检查,这是⼀种预防机制,确保在第⼀时间发现死锁并进⾏解决。
2、公平锁和⾮公平锁的底层实现?
公平锁和⾮公平锁,它们的底层实现都会使⽤AQS来进⾏排队。
它们的区别在于:
- 如果是公平锁,会先检查AQS队列中是否存在线程在排队,如果有线程在排队,则当前线程也进⾏排队。
- 如果是⾮公平锁,则不会去AQS队列中检查是否有线程在排队,⽽是直接竞争锁。
不管是公平锁还是⾮公平锁,⼀旦没竞争到锁,都会进⾏排队,当锁释放时,都是唤醒排在最前⾯的线程,所以⾮公平锁只是体现在了线程加锁阶段,而没有体现在线程唤醒阶段。
另外,不管是公平锁还是⾮公平锁都是可重⼊的。
3、ReentrantLock中tryLock()和lock()⽅法的区别?
- 1.tryLock()表示尝试加锁,可能加到,也可能加不到,该⽅法不会阻塞线程,如果加到锁则返回true,没有加到则返回false ;
- 2.lock()表示阻塞加锁,线程会阻塞直到加到锁,⽅法也没有返回值。
4、Sychronized的偏向锁、轻量级锁、重量级锁?
- 1、偏向锁:在锁对象的对象头中记录了当前获取到锁的线程ID,如果该线程⼜来获取锁,就可以直接获取到了,这就是可重入的概念;
- 2、轻量级锁:如果有另外的线程来竞争锁,偏向锁就会升级为轻量级锁,轻量级锁的底层是通过⾃旋来实现的,并不会阻塞线程;
- 3、重量级锁:如果⾃旋次数超过10次仍然没有获取到锁,则会升级为重量级锁,重量级锁会导致线程阻塞。
- 4、⾃旋锁:⾃旋就是线程在获取锁的过程中,不会去阻塞线程,是线程通过CAS获取预期的⼀个标记,如果没有获取到,则继续循环获取,如果获取到了则表示获取到了锁,这个过程线程⼀直在运⾏中,消耗的CPU资源比较少,⽐较轻量。
5、谈谈你对AQS的理解,AQS如何实现可重⼊锁?
- 1.AQS是⼀个抽象的对列同步器,在AQS中,维护了一个volatile修饰的state标识和⼀个双向链表队列(FIFO);
- 2.这个队列就是⽤来给线程排队的,⽽state就像是⼀个红绿灯,⽤来控制线程排队或者放⾏的;
- 3.在可重⼊锁的场景下,state就⽤来表示加锁的次数,0标识⽆锁,每加⼀次锁,state就加1,释放锁state就减1。
6、AQS锁的类别?
AQS分为“排他锁”和“共享锁”两种:
- 1.排他锁:是指在同一时刻只能有一个线程去占有锁,其他线程既不可以读,也不可以写,比如ReentrantLock;
- 2.共享锁:是指在同一时刻可以有多个线程去占有锁,其他线程可以读,但不可以写,比如CountDownLatch计数器和Semaphore信号量;
另外AQS也支持同时实现独占和共享两种方式,比如ReentrantReadWriteLock。
7、CountDownLatch和Semaphore的区别和底层原理?
底层都是通过AQS实现的。
- CountDownLatch计数器:比如某个任务分为N个子线程并发去执行,state 也初始化为N;每个子线程执行完后调用countDown方法,state会减1,当所有子线程都执行完后,即state=0 ,然后会依次去唤醒AQS中排队的线程。
- Semaphore信号量:我们常常用它来控制对有限资源的访问,每次使用资源前,先申请一个信号量,如果信号量不够,就会阻塞等待,并通过AQS来排队,当某个线程释放资源后,就释放一个信号量,然后会依次去唤醒AQS中排队的线程。
8、ReentrantLock底层原理?
底层是通过AQS实现的,state 初始化为 0,表示无锁状态;A 线程调用lock(加锁) 时,会将 state加 1 ,直到 A 线程调用 unlock(释放锁) 到 state=0为止,其它线程才有机会获取该锁。当然,释放锁之前,A 线程自己是可以重复获取该锁的,state 会累加,这就是可重入的概念。但要注意,加了多少次就锁要释放多少次,这样才能保证state恢复到0。
9、Sychronized和ReentrantLock的区别?
- 1、Sychronized是⼀个关键字,ReentrantLock是⼀个类;
- 2、Sychronized会自动的加锁和释放锁,ReentrantLock需要手动加锁和释放锁;
- 3、Sychronized是非公平锁,ReentrantLock可以选择公平锁或非公平锁(默认);
- 4、Sychronized是JVM层⾯的锁,ReentrantLock是API层⾯的锁。
- 5、ReentrantLock有个tryLock()方法,尝试抢占锁,不会造成阻塞,加到锁返回true。
理解第4点:Sychronized锁的是对象,锁信息保存在JVM对象头中,ReentrantLock是通过AQS中一个volatile修饰的state 来标识锁的状态。
10、Synchronized 偏向锁有个开关,如果默认开启有什么缺点?
JDK15默认关闭偏向锁优化,如果要开启可以使用XX:+UseBiasedLocking,但使用偏向锁相关的参数都会触发deprecate警告。
原因:在现在的jdk中,偏向锁加锁时 带来的性能提升 从整体上看并没有过多的收益,但撤销锁的成本过高,需要在一个安全点停止拥有锁的线程,使其变成无锁状态。
11、CAS与传统synchronized区别
CAS工作原理是基于乐观锁且操作是原子性的,与synchronized的悲观锁(底层需要调用操作系统的mutex锁)相比,效率也会相对高一些。
12、CAS是不是操作系统执行的?
不是,CAS是主要是通过处理器的指令来保证原子性的 。
13、说一下CAS怎么用的,会有哪些问题?
CAS就是我们所说的比较和交换,是采用的乐观锁技术,来实现线程安全的问题。
CAS有三个属性:旧值(A)、新值(B)、内存对象(V);
CAS原理就是对V进行赋值时,先判断原来的值是否为A,如果为A,就把新值B赋值到V,如果原来的值不是A(代表V的值放生了变化),那么会通过循环再走CAS流程,直到能够把新值B赋值成功(会给CPU带来很大的开销)。
CAS会有ABA的问题,可以通过加版本号,更新的时候时候不仅比较值,还比较版本号。
14、cas自增 和 Synchronized 自增 谁快?
- 1、在线程数较少的时候,CAS实现比较快,性能优于synchronized,因为synchronized是悲观锁,存在锁竞争,会造成阻塞。
- 2、当线程数大于一定数量的时候,CAS性能就不如synchronized了,因为多个线程在循环调用CAS接口,虽然不会让其他线程阻塞,但是这个时候竞争激烈,会导致CPU到达100%,同时会消耗更多时间。
15、Synchronized 和 ReentracLock 哪个快,为啥?
Synchronized 底层实现由JVM保证:在JVM运行过程中,可能出现偏向锁,轻量级锁,重量级锁。
- 偏向锁:当线程第一次获取到锁的时候,将对象头中的 mark word 中的偏向锁线程的标识设为自己的id,当有其他线程竞争锁的时候,发现偏向锁线程的标识并不是自己,会进行一次 CAS替换,如果不成功,就会将锁升级为轻量级锁(消耗极少);
- 轻量级锁:当前线程会将对象头中的 mark Word 复制到自己的栈空间中,然后通过自旋来获取锁,自旋10次还是获取锁失败,说明当前锁存在竞争,会将对象头的锁标识改为重量级指针,锁会膨胀为重量级锁(消耗:复制和自旋);
- 重量级锁:需要操作系统实现线程之间的切换,这就需要从用户态转换到内核态,这个成本非常高,状态之间的转换需要相对比较长的时间。
- ReentrantLock 底层是基于 AQS 实现:AQS 内部维护了一个volatile修饰的state标识以及⼀个双向链表队列(FIFO),volatile 消耗小于 Synchronized 。
因此性能比较:偏向锁 > 轻量级锁 > ReentrantLock > Synchronized。
16、怎么检测一个线程是否拥有锁?
在 java.lang.Thread 中有一个 holdsLock()方法,拥有锁会返回 true 。
17、事务未提交而锁提前释放了?
高并发情况下,数据库事务未提交,但是锁已经释放。
- 1、把整个事务单独封装成一个方法, 放在锁的工作范围之内;
- 2、手动提交事务,因为事务是方法结束后才提交,我们可以手动提交事务;
- 3、采用的Redisson可重入锁,提供watchdog机制,在锁释放前默认每10s重置锁失效时间为30s。
18、AtomicInteger计数器自增到一万以后,怎么归零?
volatile仅仅保证变量在线程间保持可见性,却依然不能保证非原子性的操作,还是用AtomicInteger类。
使用AtomicInteger.set(0)或.getAndSet(0)。
GIT
19、什么是git?
Git是一款开源的分布式版本控制系统。
20、列举工作中常用的git命令:
- 将项目下载至本地:git clone http://xxx.git;
- 提交文件:git commit -m “修复bug”;
- 多次提交合成一次提交:git rebase -i HEAD~n;
- 推送代码:git push origin dev;
- 拉取并合并代码:git pull origin dev 等价于 git fetch origin dev + git merge origin/dev;
- 查看历史记录:git log ;
- 撤回(回退)版本:git revert(reset --hard) 提交id;
- 创建并切换分支:git checkout -b dev 等价于git branch dev + git checkout dev;
- 查看本地(远程/所有)分支:git branch(-r/-a);
- 备份(恢复)当前工作区的内容:git stash(pop)
总结
都已经看到这里啦,赶紧收藏起来,祝您工作顺心,生活愉快!
这篇关于Sychronized和ReentrantLock锁 面试题的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!