

Java面试专题-多线程(3)-原子操作
source link: https://segmentfault.com/a/1190000038405933
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

开篇介绍
大家好,公众号【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准时推送技术文章,让你的上班路不在孤独,而且每月还有送书活动,助你提升硬实力!
Recommend
-
65
-
61
前言 对于Java多线程,接触最多的莫过于使用synchronized,这个简单易懂,但是这synchronized并非性能最优的。今天我就简单介绍一下几种锁。可能我...
-
49
一:原子操作CAS(compare-and-swap) 原子操作分三步:读取addr的值,和old进行比较,如果相等,则将new赋值给*addr,他能保证这三步一起执行完成,叫原子操作也就是说它不能再分了,当有一个CPU在访问这块内容addr时,其他CPU就...
-
88
原子操作 对于一个Go程序来说,GO语言运行时系统中的调度器会恰当的安排其中所有的goroutine的运行。不过,在同一时刻,只会有少数的goroutine真正处于运行状态。为了公平起见,调度器会频繁的切换这些goroutine。这个中断的...
-
33
概念 原子操作,意思就是执行的过程不能背终端的操作。在针对某个值的原子操作执行过程中,cpu不会再去执行其他针对这个值得操作。在底层, 这会由CPU提供芯片级别的支持 ,所以绝对有效。即使在拥...
-
11
一、线程池的构造 使用线程池离不开ThreadPoolExecutor类,该类实现了ExecutorService接口,其构造方法如下: public ThreadPoolExecutor(int corePoolS...
-
9
开篇介绍 大家好,公众号【Java极客思维】近期会整理一些Java高频面试题分享给小伙伴,也希望看到的小伙伴在找...
-
7
一、定义:什么是线程安全性 当多个线程访问某个类时,不管运行时环境采用 何种调度方式 或者这些进程将如何交替执行,并且在主调代码中 不需要任何额外的同步或协同 ,这个类都能表现出 正确的行为...
-
4
Java 中 long 是不是原子操作?发布于 8 月 25 日Java中long和double的原子性java中基本类型中,long和double的长度都是8个字节,32位(4...
-
6
Java中原子操作的比较和交换 (CAS) 在本文中,我们将深入研究
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK