면접관이 질문합니다. ABA 문제가 무엇인지 아시나요?
사향 고양이가 왕자와 교환합니다
문제를 설명하기 전에 짧은 이야기를 살펴보겠습니다.
북송 왕조의 진종 황후가 죽은 후, 그의 두 당시 사랑받았던 후궁 유후궁과 리후궁은 모두 임신을 한 상태였다. 누구든 아들을 낳으면 정궁이 될 수 있음은 자명하다. 유후궁(劉孫)은 이후후가 아들을 낳고 왕비가 될까봐 오랫동안 질투하여 궁궐 관리인 두당(郡堂) 곽회(郭淮)와 조산사 이씨 유시(吉師)의 협력을 받아 계획을 세웠다. 출산 중 혼수상태로 사망했는데, 뜻밖에도 사향고양이의 털이 벗겨져 피가 흐르고 윤기가 나며 새로 태어난 왕자를 빼앗아갔다. 유후궁은 궁녀 구주에게 태자를 목졸라 죽이라고 명령했다. 구주는 참지 못하고 몰래 태자를 내시 진린에게 넘겨주었다. 진림은 태자를 여행가방에 담아 팔현왕에게 보냈다. 그를 키우기 위해. 게다가 진종은 가죽이 벗겨진 사향고양이를 보고 리후궁이 괴물을 낳았다고 생각하여 그녀를 차가운 궁궐로 강등시켰다. 곧 유후궁이 진통을 당해 아들을 낳았고, 유후후도 왕비로 임명되었습니다. 뜻밖에도 6년 후, 유왕후의 아들이 병으로 사망했습니다. Zhenzong은 더 이상 상속자가 없었기 때문에 형 Baxianwang (실제로는 그해에 교체 된 왕자)의 아들을 양자로 입양하여 왕세자로 세웠습니다.
이 이야기를 보면 왕자가 태어났을 때 사향고양이로 바뀌었다가, 이상한 상황의 조합으로 인해 마침내 왕자가 되기 위해 돌아왔다는 것을 알 수 있습니다. 결과는 같아도 그 과정은 우여곡절이 많고, 왕자는 정말 불행한 운명을 맞이하게 된다.
이 이야기를 하는 이유는 무엇인가요? 사실 오늘 소개할 문제와도 많은 관련이 있습니다. 같은 결과로 중간에 몇번의 연산이 일어났는지 모르니 변하지 않았다고 볼 수 있을까요? 다양한 비즈니스 시나리오에서 우리는 이 문제를 신중하게 고려해야 합니다.
ABA 문제 설명
멀티 스레드 시나리오CAS
会出现ABA
문제에서 ABA 문제에 대한 간단한 과학은 다음과 같습니다. 예를 들어 동일한 값에 대해 CAS 작업을 수행하는 두 개의 스레드가 있습니다. A) 동시에 이 세 가지 스레드는 다음과 같습니다.
스레드 1, 예상 값은 A, 업데이트할 값은 B 스레드 2, 예상 값은 A, 업데이트할 값은 B
스레드 1이 CPU 타임 슬라이스를 가져옵니다. 먼저 스레드 2가 다른 이유로 CPU 타임 슬라이스를 먼저 가져오는 동안 스레드 1의 값이 A의 예상 값과 비교되어 동일한 것으로 확인된 다음 값이 B로 업데이트됩니다. 이때, 스레드 3이 나타나고, 예상값은 B, 업데이트할 값은 A이며, 스레드 3의 값은 예상값과 동일합니다. B값을 비교하여 같다면 업데이트합니다. 이때 스레드 2는 블로킹에서 복구되어 CPU 타임 슬라이스를 획득합니다. 이때 스레드 2의 값을 예상 값 A와 비교하여 동일한 것으로 확인되면 값을 업데이트합니다. B.에게 스레드 2도 작업을 완료했지만 스레드 2는 값이 A->B->A의 변경 과정을 거쳤다는 사실을 알지 못합니다.
구체적인 예를 들어주세요
샤오밍이 현금인출기에서 50위안을 인출했는데 현금인출기 문제로 실이 2개 있었는데 동시에 잔액이 100에서 50으로 바뀌었습니다. 시간:
-
스레드 1(현금 지급기): 현재 값 100을 가져오고 50으로 업데이트될 것으로 예상됩니다. 스레드 2(현금 지급기): 현재 값 100을 가져오고 50으로 업데이트될 것으로 예상됩니다. 스레드 1이 성공적으로 실행되었으며 스레드 2가 어떤 이유로 차단되었습니다. - 이 때 누군가 Xiao Ming에게 50을 송금합니다.
- 스레드 3(기본값): 현재 값 50을 가져오고 이를 예상합니다. 이때 스레드 3은 성공적으로 실행되어 잔액이 100이 됩니다.
- 스레드 2는 블록에서 복구되어 100을 얻습니다. 비교 후 계속해서 잔액을 50으로 업데이트합니다.
- 실제 잔액은
100(100-50+50)
,但是实际上变为了50(100-50+50-50)
ABA 문제를 해결하려면 메모리 위치 V의 값이 수정될 때마다 버전 번호가 1씩 증가합니다.
코드 예시
AtomicStampedReference를 통한 ABA 문제 해결
AtomicStampedReference는 내부적으로 객체 값과 버전 번호를 유지 관리합니다. 초기값을 전달하고 초기 버전 번호
AtomicStampedReference가 개체 값을 설정할 때 쓰기가 성공하려면 개체 값과 상태 스탬프가 모두 예상 값을 충족해야 합니다.
private static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<Integer>(100,1); public static void main(String[] args) { //第一个线程 new Thread(() -> { System.out.println("t1拿到的初始版本号:" + atomicStampedReference.getStamp()); //睡眠1秒,是为了让t2线程也拿到同样的初始版本号 try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } atomicStampedReference.compareAndSet(100, 101,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1); atomicStampedReference.compareAndSet(101, 100,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1); },"t1").start(); // 第二个线程 new Thread(() -> { int stamp = atomicStampedReference.getStamp(); System.out.println("t2拿到的初始版本号:" + stamp); //睡眠3秒,是为了让t1线程完成ABA操作 try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("最新版本号:" + atomicStampedReference.getStamp()); System.out.println(atomicStampedReference.compareAndSet(100, 2019,stamp,atomicStampedReference.getStamp() + 1) + "\t当前值:" + atomicStampedReference.getReference()); },"t2").start(); }
1. 초기 값 100, 초기 버전 번호 1
2. 스레드 t1과 t2는 동일한 초기 버전 번호를 얻습니다
3. 스레드 t1은 ABA 작업을 완료하고 버전 번호는 3으로 증가합니다. .Thread t2 CAS 작업이 완료되었습니다. 이전에 스레드 t2에서 얻은 버전 번호 1과 동일하지 않습니다.
실행 결과:
t1拿到的初始版本号:1 t2拿到的初始版本号:1 最新版本号:3 false 当前值:100
AtomicMarkableReference
를 통해 ABA 문제를 해결하세요. AtomicMarkableReference의 유일한 차이점은 참조를 식별하기 위해 더 이상 int를 사용하지 않고 참조 변수가 변경되었는지 여부를 나타내기 위해 부울 변수를 사용한다는 것입니다.
private static AtomicMarkableReference<Integer> atomicMarkableReference = new AtomicMarkableReference<Integer>(100,false); public static void main(String[] args) { // 第一个线程 new Thread(() -> { System.out.println("t1版本号是否被更改:" + atomicMarkableReference.isMarked()); //睡眠1秒,是为了让t2线程也拿到同样的初始版本号 try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } atomicMarkableReference.compareAndSet(100, 101,atomicMarkableReference.isMarked(),true); atomicMarkableReference.compareAndSet(101, 100,atomicMarkableReference.isMarked(),true); },"t1").start(); // 第二个线程 new Thread(() -> { boolean isMarked = atomicMarkableReference.isMarked(); System.out.println("t2版本号是否被更改:" + isMarked); //睡眠3秒,是为了让t1线程完成ABA操作 try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("是否更改过:" + atomicMarkableReference.isMarked()); System.out.println(atomicMarkableReference.compareAndSet(100, 2019,isMarked,true) + "\t当前值:" + atomicMarkableReference.getReference()); },"t2").start(); }
AtomicStampedReference
可以给引用加上版本号,追踪引用的整个变化过程,如:A -> B -> C -> D -> A,通过AtomicStampedReference,我们可以知道,引用变量中途被更改了3次。但是,有时候,我们并不关心引用变量更改了几次,只是单纯的关心是否更改过,所以就有了AtomicMarkableReference
1. 초기 값은 100이며 초기 버전 번호는 수정되지 않았습니다. false 2. 스레드 t1과 t2는 동일한 초기 버전 번호를 가지며 수정되지 않았습니다. false실행 결과. :3. , 버전 번호가 수정되었습니다.
4. 스레드 t2가 CAS 작업을 완료했습니다. 버전 번호는 이전에 스레드 t2에서 얻은 버전 번호 false와 동일하지 않습니다.
以上是本期关于CAS领域的一个经典ABA问题的解析,不知道你在实际的工作中有没有遇到过,但是在面试中这块是并发知识考查的重点。如果你还没接触过此类的问题,我的建议是你自己将上面的代码运行一下,结合理论去理解一下ABA问题所带来的问题以及如何解决他,这对你日后的开发工作也是有莫大的帮助的! 위 내용은 면접관이 질문합니다. ABA 문제가 무엇인지 아시나요?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!t1版本号是否被更改:false
t2版本号是否被更改:false
是否更改过:true
false 当前值:100
多说几句

핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

AI Hentai Generator
AI Hentai를 무료로 생성하십시오.

인기 기사

뜨거운 도구

메모장++7.3.1
사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전
중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기
강력한 PHP 통합 개발 환경

드림위버 CS6
시각적 웹 개발 도구

SublimeText3 Mac 버전
신 수준의 코드 편집 소프트웨어(SublimeText3)

뜨거운 주제











CAS(CentralAuthenticationService)를 통한 PHP 보안 검증 인터넷의 급속한 발전과 함께 사용자 권한 관리 및 신원 검증이 더욱 중요해지고 있습니다. 웹 애플리케이션을 개발할 때 사용자 데이터를 보호하고 무단 액세스를 방지하는 것이 중요합니다. 이 목표를 달성하기 위해 PHP 보안 검증을 위해 CAS(CentralAuthenticationService)를 사용할 수 있습니다. 카스

1. 여러 스레드가 동시에 리소스에 대한 CAS 작업을 수행하면 하나의 스레드만 성공하지만 다른 스레드는 차단되지 않으며 다른 스레드는 작업이 실패했다는 신호만 받게 된다고 설명합니다. CAS는 실제로 낙관적 잠금임을 알 수 있습니다. 2. AtomInteger 코드를 따라가면 sum.misc.Unsafe가 최종적으로 호출되는 것을 확인할 수 있습니다. Unsafe라는 이름을 보세요. 이는 Java 클래스 및 가시성 규칙의 올바른 허점을 이용하는 안전하지 않은 클래스입니다. 속도를 위해 Unsafe는 Java의 보안 표준을 일부 절충합니다. publicfinalnativebooleancompareAndSwapInt(Object

CASCAS란 CompareAndSwap, 즉 비교하고 교환하는 것입니다. CAS가 잠금을 사용하지 않지만 동시 조건에서 안전한 데이터 조작을 보장하는 이유는 무엇입니까? 이름은 실제로 CAS의 원리를 매우 직관적으로 보여줍니다. 데이터를 수정하는 구체적인 프로세스는 다음과 같습니다. CAS를 사용하여 데이터를 조작할 때 데이터의 원래 값은 다음과 같습니다. 그리고 수정될 값은 메소드에 전달하여 현재 타겟 변수 값이 전달된 원래 값과 동일한지 비교합니다. 동일하다면 타겟 변수가 다른 스레드에 의해 수정되지 않았다는 의미입니다. 대상 변수 값을 직접 수정하면 됩니다. 대상 변수 값이 원래 값과 다른 경우에는 대상 변수가 다른 스레드에 의해 수정되었음을 증명합니다. 이 CAS 수정은 실제로 실패했음을 알 수 있습니다. 안전한 데이터 수정을 보장하지만, 수정이 실패하는 경우도 있습니다.

CAS 설명: CAS(비교 및 교환), 비교 및 교환. 다중 스레드 병렬 상황에서 잠금 사용으로 인해 발생하는 성능 손실을 해결할 수 있는 메커니즘입니다. CAS 연산에는 메모리 위치(V), 예상 원래 값(A), 새 값(B)의 세 가지 피연산자가 포함됩니다. 메모리 위치의 값이 예상되는 원래 값과 일치하면 프로세서는 자동으로 위치 값을 새 값으로 업데이트합니다. 그렇지 않으면 프로세서는 아무 작업도 수행하지 않습니다. 스레드는 주 메모리에서 num 값을 가져오고 num에서 작동합니다. 값을 쓸 때 스레드는 얻은 첫 번째 num 값을 주 메모리의 num 값과 비교합니다. 변경된 값은 num입니다. 주 메모리에 기록되지 않으면 성공할 때까지 비교가 반복됩니다. CAS에서 만든

잠긴 동시성 대부분의 프로그래머(물론 나도 기본적으로 그 중 한 명임)에게 동시 프로그래밍은 관련 데이터 구조에 잠금(Mutex)을 추가하는 것과 거의 동일합니다. 예를 들어 동시성을 지원하는 스택이 필요한 경우 가장 간단한 방법은 단일 스레드 스택에 잠금 std::sync::Mutex를 추가하는 것입니다. (여러 스레드가 스택의 소유권을 가질 수 있도록 Arc가 추가되었습니다.) usetd::sync::{Mutex,Arc};#[derive(Clone)]structConcurrentStack{inner:Arc,}implConcurrentStack{pubfnnew()-> Self{

프로그램에서는 100개의 스레드를 생성했고, 각 스레드는 공유변수 inc에 10,000개의 작업을 누적했습니다. 동기적으로 실행된다면 inc의 최종 값은 1,000,000이 되어야 하는데, 멀티스레딩에서는 프로그램이 동시 실행되는 것으로 알고 있습니다. 즉, 서로 다른 스레드가 동시에 주 메모리에서 동일한 값을 읽을 수 있습니다.

1. 새 springboot 프로젝트를 생성하고 종속성 org.jasig.cas.clientcas-client-support-springboot3.6.22를 도입합니다. @EnableCasClient 주석 packagecom.codetiler.demo;importorg.jasig.cas.client.boot.configuration을 구성합니다. EnableCasClient;importorg.springframework.boot.SpringApplication;importorg.spring

이번 이슈는 CAS 분야의 고전적인 ABA 문제에 대한 분석입니다. 실제 업무에서 접해 보셨을지는 모르겠지만, 인터뷰에서 동시성 지식 테스트의 초점은 이것입니다. 이런 종류의 문제가 발생하지 않았다면 위의 코드를 직접 실행해 보는 것이 좋습니다.
