开篇介绍
大家好,公众号【Java极客思维】近期会整顿一些Java高频面试题分享给小伙伴,也心愿看到的小伙伴在找工作过程中可能用失去!本章节次要针对Java一些多线程高频面试题进行分享。
告诉:公众号【Java极客思维】正在送书福利流动,关注公众号并加入福利流动吧!只有参加了本次流动的小伙伴才可能参加年底的大福利,不要错过呀~
Q1:
什么是CAS算法?
CAS(compare and swap)的缩写。
Java利用CPU的CAS指令,同时借助JNI来实现对Java的非阻塞算法,实现原子操作(其实就是自旋操作,一直循环,直到胜利)。其它原子操作都是利用相似的个性来实现的。
CAS有三个要害操作值:内存值V、 预期值A 、 要批改的值B 。
当且仅当 预期值A 和 内存值V 统一时,才会将 内存值V 内容批改为 B,否则将什么都不做。
CAS的毛病也很显著:
在并发量比拟高的状况下,如果许多线程重复尝试更新某一个变量,然而却又始终更新不胜利,始终在循环(自旋),那么会给CPU带来很大的压力。
CAS机制所保障的只是一个变量的原子性操作,而不能保障整个代码块的原子性。比方须要保障3个变量独特进行原子性的更新,这样就不得不应用synchronized关键字进行同步操作了。
比方线程A端了一杯水放在桌子上,然而被其余事调度走了,并且开释了锁,此时线程B通过,看到桌子上的水,端起来喝了半杯,而后又给打满一杯水放在桌子上。此时尽管还是一杯水,然而杯中的水不再是原来的那杯水了,而线程A也忙完了,回头来看到桌子上还是一杯水,然而不晓得水曾经被替换过了。这就是典型的ABA问题,还有很多相似的场景。这种状况对依赖过程值的情景的运算后果影响很大。这是CAS机制最大的问题所在。
- CPU开销过大
- 不能保障代码块的原子性
- ABA问题
Q2:
什么是AQS?
AQS(AbstractQueuedSynchronizer)
AQS是JDK下提供的一套用于实现基于FIFO期待队列的阻塞锁和相干的同步器的一个同步框架。这个抽象类被设计作为一些可用原子int值来示意状态的同步器的基类。
如果有看过相似CountDownLatch类的源码实现,会发现其外部有一个继承了AbstractQueuedSynchronizer的外部类Sync。可见CountDownLatch是基于AQS框架来实现的一个同步器。相似的同步器在JUC下还有不少(比方Semaphore)。
AQS的核心思想是基于volatile int state 这样的volatile变量,配合Unsafe工具对其原子性的操作来实现对以后锁状态进行批改。同步器外部依赖一个FIFO的双向队列来实现资源获取线程的排队工作。
AQS中的数据结构 - 节点和同步队列
节点退出到同步队列
首节点变动
Q3:
volatile关键字有什么用?
Java提供了volatile关键字来保障可见性。当一个共享变量被volatile润饰时,它会保障批改的值会立刻被更新到主内存中,当有其余线程须要读取时,它会去内存中读取新值。次要的原理是应用了内存指令。
- LoadLoad重排序:一个处理器先执行一个L1读操作,再执行一个L2读操作;然而另外一个处理器看到的是先L2再L1;
- StoreStore重排序:一个处理器先执行一个W1写操作,再执行一个W2写操作;然而另外一个处理器看到的是先W2再W1;
- LoadStore重排序:一个处理器先执行一个L1读操作,再执行一个W2写操作;然而另外一个处理器看到的是先W2再L1;
- StoreLoad重排序:一个处理器先执行一个W1写操作,再执行一个L2读操作;然而另外一个处理器看到的是先L2再W1。
Q4:
形容一下volatile关键字对原子性、可见性以及有序性是如何保障的?
在volatile变量写操作的后面会退出一个Release屏障,而后再之后会退出一个Store屏障,这样就能够保障volatile写跟Release屏障之前的任何读写操作都不会指令重排,而后Store屏障保障了,写完数据之后,立马会执行flush处理器缓存的操作。
在volatile变量读操作的后面会退出一个Load屏障,这样就能够保障对这个变量的读取时,如果被别的处理器批改过了,必须得从其余处理器的高速缓存(或者主内存)中加载到本人本地高速缓存里,保障读到的是最新数据;在之后会退出一个Acquire屏障,禁止volatile读操作之后的任何读写操作会跟volatile读指令重排序。
与volatile读写内存屏障比照一下,是相似的意思。
Acquire屏障 其实就是 LoadLoad屏障 + LoadStore屏障;
Release屏障 其实就是 StoreLoad屏障 + StoreStore屏障。
Q5:
简述一下synchronized关键字的原理是什么?
synchronized是由JVM实现的一种实现互斥同步的形式,查看被synchronized关键字润饰过的程序块编译后的字节码会发现:被synchronized润饰过的程序块,在编译前后被编译器生成了monitorenter 和 monitorexit 两个字节码指令。
在虚拟机执行到 monitorenter 指令时,首先会尝试获取对象的锁:如果这个对象没有锁定,或者以后线程曾经领有了这个对象的锁,把锁的计数器 + 1;
当执行 monitorexit 指令时,将锁计数器 - 1;当计数器为0时,锁就被开释了。如果获取对象失败了,那以后线程就要阻塞期待,晓得对象锁被另外一个线程开释为止。
Q6:
CountDownLatch 和 CyclicBarrier的区别?
- CountDownLatch的计数器只能应用一次。而CyclicBarrierd的计数器能够应用reset()办法进行重置。所以CyclicBarrier能解决更为简单的业务场景。比方如果计算产生谬误,能够重置计数器,并让线程们从新计算一次。
- CyclicBarrier还提供其余有用的办法,比方getNumberWaiting()办法能够取得CyclicBarrier阻塞的线程数量。isBroken() 办法能够用来晓得阻塞的线程是否被中断。
- CountDownLatch会阻塞主线程,CyclicBarrier不会阻塞主线程,只会阻塞子线程。
CountDownLatch依附一个外力(计数器、发令枪)来控制线程,而CyclicBarrier是相当于用自身来控制线程。举个例子:
- CountDownLatch:有一个装满宝石的房间,门外有7把锁,而后有7集体要进入房间组成队伍A,还有另外7集体手里拿着钥匙组成队伍B,那么首先A组的7集体必须等到B组的7集体把钥匙送过来,而后把门外的7把锁别离关上之后,这A组的7个人才可能进入房间拿到宝石。
- CyclicBarrier:有一个装满宝石的房间,门外也有7把锁,而后7集体必须到另外的一个房间,各自实现一个工作之后,这7个人才可能各自取得一把锁,实现工作之后,这7集体就可能关上那7把锁,进房间拿到宝石。
今天,会介绍多线程一些深刻的常识,长按二维码关注我吧~
祝大家都能拿到心仪的offer!
点关注、不迷路
如果感觉文章不错,欢送关注、点赞、珍藏,你们的反对是我创作的能源,感激大家。
如果文章写的有问题,请不要悭吝,欢送留言指出,我会及时核查批改。
如果你还想更加深刻的理解我,能够微信搜寻「Java极客思维」进行关注。每天8:00准时推送技术文章,让你的下班路不在孤单,而且每月还有送书流动,助你晋升硬实力!