Java 동시 프로그래밍 시리즈의 추가 부분C A S(비교 및 교환)
, 기사 스타일은 여전히 이해하기 쉬운 그림과 텍스트로 가득 차 있어 독자가 면접관과 미친 대화를 나눌 수 있습니다. C A S(Compare and swap)
,文章风格依然是图文并茂,通俗易懂,让读者们也能与面试官疯狂对线。
C A S
作为并发编程必不可少的基础知识,面试时C A S
也是个高频考点,所以说C A S
是必知必会,本文将带读者们深入理解C A S
C A S code>동시 프로그래밍에 대한 필수적인 기본 지식으로 인터뷰 시<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px; 여백 왼쪽: 2px;배경 색상: rgba(27, 31, 35, 0.05); 글꼴 계열: " operator mono consolas monaco menlo monospace break-all rgb>C A S
도 고주파 테스트 사이트이므로 C A S
는 꼭 알아야 할 내용이며, 이 기사는 독자들에게 심층적인 이해를 제공할 것입니다.C A S
. 🎜C A S(compareAndSwap)
也叫比较交换,是一种无锁原子算法,映射到操作系统就是一条cmpxchg
硬件汇编指令(保证原子性),其作用是让C P U
将内存值更新为新值,但是有个条件,内存值必须与期望值相同,并且C A S
작업은 사용자 모드와 커널 모드 간 전환이 필요하지 않으며, 사용자 모드에서 직접 메모리를 읽고 씁니다(블로킹/스레드가 없음을 의미). 컨텍스트 전환).
它包含3个参数<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px; background-color : rgba(27, 31, 35, 0.05); 글꼴 계열: " operator mono consolas monaco menlo monospace break-all rgb> C A S(V,E,N)
,V
表示待更新的内存值,E
表示预期值,N
表示新值,当 V
值等于E
值时,才会将V
值更新成N
值,如果V
值和E
值不等,不做更新,这就是一次C A S
적 작업。3
个参数C A S(V,E,N)
,V
表示待更新的内存值,E
表示预期值,N
表示新值,当 V
值等于E
值时,才会将V
值更新成N
值,如果V
值和E
值不等,不做更新,这就是一次C A S
的操作。
简单说,C A S
C A S
외부 발행은 个期望值, 也就是你认为这个变weight现에서 应该是什么样子의, 如果变weight는 不是你想象的那样,说明它已经被别人修改过了,你只需要重新读取,设置新期望值,再次尝试修改就好了。🎜원자성은 하나 이상의 작업이 C P U
실행 중에 중단할 수 없는 기능입니다. 실행되거나 실행되지 않으며 중간에 실행할 수 없습니다(중단될 수 없는 기능입니다) 또는 일련의 작업). C P U
执行的过程中不被中断的特性,要么执行,要不执行,不能执行到一半(不可被中断的一个或一系列操作)。
为了保证C A S
的原子性,C P U
提供了下面两种方式
总线(B U S
)是计算机组件间的传输数据方式,也就是说C P U
与其他组件连接传输数据,就是靠总线完成的,比如C P U
C PU
는 다음 두 가지 방법을 제공합니다🎜B U S
)는 컴퓨터 구성 요소 간에 데이터를 전송하는 방법입니다. 즉, C P U
는 다른 구성 요소와 연결하여 데이터를 전송합니다. 이는 C P U
는 메모리를 읽고 씁니다. 🎜总线锁定是指C P U
使用了总线锁,所谓总线锁就是使用C P U
는 LOCK#
信号,当C P U
에서 总线上输流LOCK#
信号时,其他C P U
적总线请求将被阻塞。C P U
使用了总线锁,所谓总线锁就是使用C P U
提供的LOCK#
信号,当C P U
在总线上输出LOCK#
信号时,其他C P U
的总线请求将被阻塞。
总线锁定方式虽然保证了原子性,但是在锁定期间,会导致大量阻塞,增加系统的性能开销,所以现代C P U
为了提升性能,通过锁定范围缩小的思想设计出了缓存行锁定(缓存行是C P U
C P U
为了提升性能,通过锁定范围缩작은思想设计了缓存行锁定(🎜缓存行是C P U
高速缓存存储的最小单位🎜)。🎜소위 캐시 잠금은 C PU
는 C P U
对缓存行进行锁定,当缓存行中的共享变量回写到内存时,其他C P U
会通过总线嗅探机制感知该共享变量是否发生变化,如果发生变化,让自己对应的共享变量缓存行失效,重新从内存读取最新的数据,缓存锁定是基于缓存一致性机制来实现的,因为缓存一致性机制会阻止两个以上C P U
同时修改同一个共享变量(现代C P U
基本都支持和使用缓存锁定机制)。
C A S
和锁都解决了原子性问题,和锁相比没有阻塞、线程上下文你切换、死锁,所以C A S
要比锁拥有更优越的性能,但是C A S
同样存在缺点。
C A S
캐시 라인
C P U
는 버스 스니핑 메커니즘을 통해 공유 변수가 변경되었는지 여부를 감지합니다. 해당 공유 변수 캐시 라인이 유효하지 않으며 최신 데이터를 메모리에서 다시 읽습니다. 캐시 잠금은 캐시 일관성 메커니즘을 기반으로 구현됩니다. 캐시 일관성 메커니즘은 두 개 이상의 C P U
동일한 공유 변수를 동시에 수정 (🎜ModernC P U기본적으로 모두 캐시 잠금 메커니즘을 지원하고 사용합니다 🎜). 🎜<h1 data-tool="mdnice editor" style="margin-top: 30px;margin-bottom: 15px;font-weight:bold;font-size: 24px;">
<span style="display: none; "></span>C A S 문제</h1>🎜<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px; margin- 왼쪽: 2px;배경색상: rgba(27, 31, 35, 0.05);글꼴군: " operator mono consolas monaco menlo monospace break-all rgb>C A S
및 잠금은 둘 다 원자성 문제를 해결합니다. 잠금에 비해 차단, 스레드 컨텍스트 전환 및 교착 상태가 없으므로 C A S
는 잠금보다 성능이 우수하지만C A S동일 단점이 있습니다. 🎜🎜C A S 코드의 문제점은 다음과 같습니다🎜<ul class="list-paddingleft-2" data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;list-style-type: square;">
<li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"><strong style="color: black;">공유 변수의 원자적 연산만 보장할 수 있습니다</strong></section></li>
<li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"><strong style="color: black;">스핀 시간이 너무 깁니다(스핀 잠금 기준)</strong></section></li>
<li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"><strong style="color: black;"><code style="overflow-wrap: break -word ;패딩: 2px 4px;테두리 반경: 4px;마진-오른쪽: 2px;마진-왼쪽: 2px;배경색: rgba(27, 31, 35, 0.05);글꼴 계열: " operator mono consolas monospace break-all rgb>ABA
QuestionABA
问题C A S
只能针对一个共享变量使用,如果多个共享变量就只能使用锁了,当然如果你有办法把多个变量整成一个变量,利用C A S
也不错,例如读写锁中state
C A S
는 하나의 공유변수에만 사용할 수 있습니다. 공유변수가 여러 개인 경우에는 잠금만 사용할 수 있습니다. 물론, C A S
도 좋습니다(예: 읽기-쓰기 잠금상태의 상위 및 하위 비트
. 🎜
스레드가 잠금 획득에 실패하면 차단 및 일시 중단되지 않고 성공할 때까지 일정 시간 후에 다시 획득을 시도합니다. 일종의 루프 획득 메커니즘을 스핀 잠금이라고 합니다(스핀락
). spinlock
)。
自旋锁好处是,持有锁的线程在短时间内释放锁,那些等待竞争锁的线程就不需进入阻塞状态(无需线程上下文切换/无需用户态与内核态切换),它们只需要等一等(自旋),等到持有锁的线程释放锁之后即可获取,这样就避免了用户态和内核态的切换消耗。
自旋锁坏处显而易见,线程在长时间内持有锁,等待竞争锁的线程一直自旋,即CPU一直空转,资源浪费在毫无意义的地方,所以一般会限制自旋次数。
最后来说自旋锁的实现,实现自旋锁可以基于C A S
实现,先定义lockValue
对象默认值1
,1
代表锁资源空闲,0
C A S
구현, 먼저 lockValue
객체 기본값1
,1
은 잠금 리소스가 사용 가능함을 의미합니다. 0
은 잠금 리소스가 사용 중임을 의미하며 코드는 다음과 같습니다🎜public class SpinLock { //lockValue 默认值1 private AtomicInteger lockValue = new AtomicInteger(1); //自旋获取锁 public void lock(){ // 循环检测尝试获取锁 while (!tryLock()){ // 空转 } } //获取锁 public boolean tryLock(){ // 期望值1,更新值0,更新成功返回true,更新失败返回false return lockValue.compareAndSet(1,0); } //释放锁 public void unLock(){ if(!lockValue.compareAndSet(1,0)){ throw new RuntimeException("释放锁失败"); } } }
AtomicInteger
类型的lockValue
变量,AtomicInteger
是Java
基于C A S
实现的Integer
原子操作类,还定义了3个函数lock、tryLock、unLock
tryLock 함수는 위에 정의되어 있습니다 - 잠금을 획득하세요
C A S
更NewC A S
更新lockValue
值相等,则lockValue
值更新为0
,返回true
,否则执行下面逻辑lockValue
值不相等,不做任何更新,返回false
如果期望值与lockValue
值上等,则lockValue
值更新为0
,返回 true
,否则执行下面逻辑
lockValue
值不任何更新,返回거짓
🎜🎜🎜🎜🎜unLock函数-释放锁🎜0
, 새로운 버전1
0
,更新值1
C A S
更新lockValue
值相等,则lockValue
值更新为1
,返回true
,否则执行下面逻辑lockValue
值不相等,不做任何更新,返回false
C A S
更新
lockValue
值상等,则lockValue
值更新为1
,返回true ,否则执行下면逻辑🎜🎜🎜🎜 🎜🎜如果期望值与lockValue
值不상等, 不做任何更新,返回false🎜🎜🎜🎜🎜lock函数-自旋获取锁🎜<ul class="list-paddingleft-2" data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;list-style-type: square;"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"><strong style="color: black;">실행<code style='overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);'>tryLock
函数,返回true
停止,否则一直循环
从上图可以看出,只有tryLock
成功的线程(把lockValue
更新为0
),才会执行代码块,其他线程个tryLock
自旋等待lockValue
被更新成1
,tryLock
成功的线程执行unLock
(把lockValue
更新为1
),自旋的线程才会tryLock
성공했습니다.
C A S
需要检查待更新的内存值有没有被修改,如果没有则更新,但是存在这样一种情况,如果一个值原来是A
,变成了B
,然后又变成了A
,在C A S
확인해 보면 수정되지 않은 것을 확인할 수 있습니다.
쓰레드가 2개 있다고 가정하면, 쓰레드1
读取到内存值A
,线程1
时间片用完,切换到线程2
,线程2
也读取到了内存值A
,并把它修改为B
值,然后再把B
值还原到A
值,简单说,修改次序是A->B->A
,接着线程1
恢复运行,它发现内存值还是A
,然后执行C A S
操作,这就是著名的ABA
가 문제지만 문제는 없는 것 같습니다.
단순한 데이터 구조이므로 실제로는 문제가 없습니다. 복잡한 데이터 구조라면 문제가 있을 수 있습니다. (AtomicReference
可以把C A S
使用在对象上),以链表数据结构为例,两个线程通过C A S
去删除头节点,假设现在链表有A->B
노드
1
删除A
节点,B
节点成为头节点, 正要执行C A S(A,A,B)
时,时间文 完,切换到线程 2
1
删除A
节点,B
节点成为头节点,正要执行C A S(A,A,B)
时,时间片用完,切换到线程2
2
删除A、B
节点2
加入C、A
节点,链表节点变成A->C
1
重新获取时间片,执行C A S(A,A,B)
C
2
删除A、B
节点🎜🎜🎜🎜线程2
加入C、A
节点,链表节点变成A->C 코드>🎜🎜🎜🎜线程1
새로 추가된 获取时间 Images 执行C A S(A, A,B)
🎜🎜🎜🎜丢失C
节点🎜🎜
해결하려면A B A
问题也非常简单,只要追加版本号即可,每次改变时加1
,即A —> B —> A
,变成1A —> 2B —> 3A
,在Java
中提供了AtomicStampedRdference
可以实现这个方案(面试只要问了C A S
,就一定会问ABA
이것을 이해해야 합니다).
위 내용은 초보자도 BAT 면접관과 경쟁할 수 있습니다: CAS의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!