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>동시 프로그래밍에 대한 필수적인 기본 지식으로 인터뷰 시C A S
도 고주파 테스트 사이트이므로
C A S
는 꼭 알아야 할 내용이며, 이 기사는 독자들에게 심층적인 이해를 제공할 것입니다.
C A S
.
개요
CA S의 기본 개념
C A S(compareAndSwap)
也叫比较交换,是一种无锁原子算法,映射到操作系统就是一条cmpxchg
硬件汇编指令(保证原子性),其作用是让C P U
将内存值更新为新值,但是有个条件,内存值必须与期望值相同,并且C A S
작업은 사용자 모드와 커널 모드 간 전환이 필요하지 않으며, 사용자 모드에서 직접 메모리를 읽고 씁니다(블로킹/스레드가 없음을 의미). 컨텍스트 전환).
它包含3个参数 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 A S가 원자성을 보장하는 방법
원자성은 하나 이상의 작업이C P U
실행 중에 중단할 수 없는 기능입니다. 실행되거나 실행되지 않으며 중간에 실행할 수 없습니다(중단될 수 없는 기능입니다) 또는 일련의 작업).C P U
执行的过程中不被中断的特性,要么执行,要不执行,不能执行到一半(不可被中断的一个或一系列操作)。
为了保证C A S
的原子性,C P U
提供了下面两种方式
-
总线锁定
-
缓存锁定
总线锁定
总线(B U S
)是计算机组件间的传输数据方式,也就是说C P U
与其他组件连接传输数据,就是靠总线完成的,比如C P U
rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">C A S의 원자성,
C PU
는 다음 두 가지 방법을 제공합니다
-
버스 잠금 장치
-
캐시 잠금
ul>
버스 잠금
버스(
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
缓存锁결정
总线锁定方式虽然保证了原子性,但是在锁定期间,会导致大weight阻塞,增加系统的性能开销,所以现代
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 A S
캐시 라인
을 잠그고, 캐시 라인의 공유 변수가 메모리에 다시 기록되면 다른
C P U
는 버스 스니핑 메커니즘을 통해 공유 변수가 변경되었는지 여부를 감지합니다. 해당 공유 변수 캐시 라인이 유효하지 않으며 최신 데이터를 메모리에서 다시 읽습니다. 캐시 잠금은 캐시 일관성 메커니즘을 기반으로 구현됩니다. 캐시 일관성 메커니즘은 두 개 이상의
C P U
동일한 공유 변수를 동시에 수정 (Modern
C P U기본적으로 모두 캐시 잠금 메커니즘을 지원하고 사용합니다 ). C A S 문제
C A S
및 잠금은 둘 다 원자성 문제를 해결합니다. 잠금에 비해 차단, 스레드 컨텍스트 전환 및 교착 상태가 없으므로
C A S
는 잠금보다 성능이 우수하지만C A S동일 단점이 있습니다.
C A S 코드의 문제점은 다음과 같습니다 공유 변수의 원자적 연산만 보장할 수 있습니다 스핀 시간이 너무 깁니다(스핀 잠금 기준) ABA
Question
ABA
问题
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
值不상等, 不做任何更新,返回
falselock函数-自旋获取锁실행tryLock
函数,返回
true
停止,否则一直循环
从上图可以看出,只有tryLock
成功的线程(把lockValue
更新为0
),才会执行代码块,其他线程个tryLock
自旋等待lockValue
被更新成1
,tryLock
成功的线程执行unLock
(把lockValue
更新为1
),自旋的线程才会tryLock
성공했습니다.
ABA 문제
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 중국어 웹사이트의 기타 관련 기사를 참조하세요!