자바 CAS 에 관 한 지식 원 리 를 간단히 이해 하 다.

JMM 과 문제 도입
왜 먼저 JMM 을 말 합 니까?CAS 의 실현 클래스 에서 유지 하 는 변 수 는 모두 volatile 에 의 해 수식 되 기 때 문 입 니 다.이 volatile 은 JMM 규범(100%따 르 는 것 이 아니 라 다음 글 에서 말 합 니 다)에 따라 다 중 스 레 드 를 병행 하여 특정한 변 수 를 방문 하여 스 레 드 안전 을 실현 하 는 수단 입 니 다.
일련의 지식 이 천천히 이어지다.

먼저 JMM 이 무엇 인지,JMM 은 바로 여러분 이 말 하 는 자바 의 메모리 모델 입 니 다.이것 은 사람들 이 논리 적 으로 구분 하거나 JMM 을 규범 으로 볼 수 있 습 니 다.어떤 규범 이 있 습 니까?아래 와 같다
4.567917.가시 성:특정한 스 레 드 가 메모리 의 변 수 를 변경 한 후에 다른 스 레 드 가 첫 번 째 사건 에서 바로 통 지 를 받 도록 요구한다.CAS 의 실현 에서 가시 성 은 수 동적 으로 통 지 를 받 는 것 이 아니 라 끊 임 없 는 while 순환 을 통 해 읽 는 것 이다.
원자 성:스 레 드 가 특정한 조작 을 수행 할 때 함께 성공 하거나 함께 실패 합 니 다4.567917.질서 성:성능 을 향상 시 키 기 위해 컴 파 일 러 프로 세 서 는 명령 의 정렬 을 다시 하고 소스 코드->컴 파 일 러 최적화 정렬->프로세서 최적화 정렬->메모리 시스템 정렬->최종 실행 명령JVM 이 실행 하 는 실 체 는 스 레 드 입 니 다.모든 스 레 드 가 생 성 된 후에 JVM 은 작업 공간 을 만 듭 니 다.이 작업 공간 은 모든 스 레 드 간 의 개인 공간 이 고 그 어떠한 스 레 드 간 에 도 상대방 의 작업 공간,스 레 드 간 의 통신 에 직접 접근 할 수 없습니다.반드시 공유 공간 을 통 해 중간 에 완성 해 야 합 니 다.
JMM 은 모든 변수 가 메 인 메모리 에 존재 하도록 규정 하고 있 습 니 다.메 인 메모 리 는 공유 공간 입 니 다.만약 에 특정한 스 레 드 가 메 인 메모리 에 있 는 공유 변 수 를 수정 하면 어떻게 합 니까?닮다
다음은 이렇게:
공유 변수의 복사 본 을 작업 공간 에 복사 합 니 다
  • 변 수 를 할당 수정 합 니 다
  • 4.567917.작업 공간의 변 수 를 메모리 에 다시 씁 니 다.4.567918.
    JMM 은 다음 과 같이 규정 하고 있다.
    4.567917.모든 스 레 드 는 잠 금 을 풀기 전에 작업 공간의 공유 변 수 를 메모리 에 즉시 갱신 해 야 합 니 다4.567917.스 레 드 는 잠 금 을 추가 하기 전에 메 인 메모리 의 값 을 읽 고 자신의 작업 공간 으로 업데이트 해 야 합 니 다4
  • 자물쇠 와 잠 금 해 제 는 같은 자물쇠 입 니 다
  • 문제 도입
    이때 여러 개의 스 레 드 가 동시에 위의 세 단계 로 메 인 메모리 의 공유 변 수 를 방문 하면 스 레 드 안전성 에 문제 가 발생 할 수 있 습 니 다.예 를 들 어 현재 메 인 메모리 의 공유 변 수 는 c=1 이 고 AB 두 개의 스 레 드 가 이 c 변 수 를 동시에 방문 하려 고 합 니 다.모두 c++를 하고 싶 습 니 다.현재 A 는 c 를 자신의 작업 공간 에 복사 하여 c+를 진행 합 니 다.그래서 c=2.이 동시에 스 레 드 B 도 c+를 진행 합 니 다.c.B 의 작업 공간 에서=2,AB 스 레 드 는 결 과 를 작업 공간 에 다시 쓰 는 최종 결 과 는 2 입 니 다.우리 가 예상 한 3 이 아 닙 니 다.
    JUC 를 사용 하면 원자 류 가 이 문 제 를 피 할 수 있다 는 것 을 잘 알 고 있 습 니 다.
    원자 류 의 밑바닥 에서 사용 되 는 것 은 바로 CAS 기술 이다.
    CAS 가 뭐야?
    CAS(compare and swap)는 말 그대로 비교 와 교환 이다.JUC 에서 원자 류 의 밑바닥 에 사용 되 는 것 은 모두 CAS 의 자물쇠 없 이 라인 안전 을 실현 하 는 훌륭 한 기술 이다.
    다음 두 줄 의 코드 를 비교 한 다음 에 교환 합 니 다.즉,메 인 메모리 에서 읽 은 값 이 4 이면 2019 로 업데이트 합 니 다.
    
      AtomicInteger atomicInteger = new AtomicInteger(4);
      atomicInteger.compareAndSet(4,2019);
    AtomicInteger 의 원본 코드 를 따라 가면 다음 과 같 습 니 다.밑 에 int 형식의 변 수 를 유지 하고 있 습 니 다.(물론 제 가 선택 한 원래 종 류 는 AtomicInteger 형식 이기 때 문 입 니 다)그리고 이 int 형식의 값 은 volatile 에 의 해 수식 되 었 습 니 다.
    
     private volatile int value;
    
     /**
      * Creates a new AtomicInteger with the given initial value.
      *
      * @param initialValue the initial value
      */
     public AtomicInteger(int initialValue) {
      value = initialValue;
     }
    volatile 이 뭐야?
    volatile 은 JVM 이 제공 하 는 경 량 동기 화 메커니즘 인 데 왜 경 량 경계 입 니까?방금 위 에서 말 했 듯 이 JMM 규범 에서 세 가지 특성 을 언급 했 는데 JVM 이 제공 한 volatile 은 위의 규범 중의 2/3 만 만족 시 켰 다.다음 과 같다.
    가시 성 보증
    원자 성 을 보장 하지 않 는 다.
    4.567917.명령 재 정렬 금지단독 volatile 은 원자 성 을 만족 시 킬 수 없다.즉,다음 코드 는 다 중 스 레 드 가 동시 방문 하 는 상황 에서 도 스 레 드 안전성 문제 가 발생 할 수 있다.
    
    private volatile int value;
     
    public void add(){
     value++; 
    }
    그렇다면 JUC 의 원자 류 는 어떻게 원자 성 을 만족 시 킬 수 있 을 까?그래서 어 쩔 수 없 이 이 영화 박문 의 주인공,CAS
    CAS 소스 코드 따라 가기
    AtomicInteger 에 따라 가 는 방법 increment AndGet()
    
     public final int incrementAndGet() {
      return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
     }
    코드 를 통 해 우 리 는 Unsafe 류 를 호출 하여 실현 하 는 것 을 보 았 다.
    Unsafe 류 가 뭐 예요?
    Unsafe 류 에 들 어가 면 그 안에 대량의 native 방법 이 존재 하 는 것 을 볼 수 있 습 니 다.이런 native 방법 들 은 모두 빈 방법 입 니 다.
    이 unsafe 클래스 는 사실 뒷문 에 해당 합 니 다.그 는 자바 가 호출 시스템 의 C C+함수 라 이브 러 리 에 접근 하 는 방법 입 니 다.아래 그림 과 같 습 니 다.

    계속 이 방법 에 따라 increment AndGet()그래서 우 리 는 우리 의 주인공 방법 에 왔 습 니 다.이 방법 에 대해 이해 하기 어렵 지 않 습 니 다.주로 방법 중의 var 12345 가 무엇 을 대표 하 는 지 알 아내 면 됩 니 다.다음 과 같은 코드+주석 입 니 다.
    
    var1:           : this,     
    var2:           valueOffset,          
                                     
      
    var4:           1,         
    var5:   this                      
    public final int getAndAddInt(Object var1, long var2, int var4) {
      int var5;
      do {
       var5 = this.getIntVolatile(var1, var2);
      } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
    
      return var5;
     }
    while 순환 을 사용 하 는 것 을 주의 하 십시오.if(flag){}에 비해 이러한 표기 법 은 한 번 더 판단 할 수 있 습 니 다.전체적인 사 고 는 수정 하기 전에 한 번 비교 하 는 것 입 니 다.읽 은 현재 값 이 예상 치 와 같 으 면 증가 합 니 다.그렇지 않 으 면 계속 문의 하여 수정 하 는 것 입 니 다.
    소총화
    위의 과정 을 통 해 사실 CAS 의 밑바닥 실현 원 리 를 총 결 해 낼 수 있다
  • volatile
  • 자물쇠
    unsafe 류
    보충:CAS 는 네 이 티 브 방법의 하부 실현 을 통 해 본질 적 으로 운영 체제 차원 의 CPU 의 병발 원 어 이다.JVM 은 어 셈 블 리 차원 의 명령 을 직접 실현 하고 하드웨어 에 의존 하여 실현 한다.또한 CPU 의 원 어 에 있어 두 가지 특성 이 있다.1.반드시 연속 되 어야 한다.2.중단 되 지 않 는 다.
    CAS 의 장단 점
    장점:
    그것 의 밑바닥 에서 우 리 는 do-while 를 통 해 실 현 된 자선 자 물 쇠 를 보고 여러 라인 사이 에서 전환 하 는 데 가 져 오 는 추가 상하 문 전환 비용 을 줄 였 다
    단점:
  • while 순환 의 끊 임 없 는 시 도 를 통 해 상하 문 전환 비용 을 절약 하지만 cpu 의 자원 을 점용 합 니 다
  • CAS 는 하나의 공유 변수의 원자 성 만 확보 할 수 있 고 여러 개의 공유 변수 가 존재 하면 잠 금 을 추가 하여 실현 해 야 합 니 다
  • ABA 문제 가 있 음ABA 문제
    ABA 문제 가 뭐야?
    우리 가 이렇게 노 는 것 은 AB 두 스 레 드 로 AtomicInteger 에 초기 값 0 을 부여 하 는 것 입 니까?
    A 라인 의 코드 는 다음 과 같 습 니 다.
    
      Thread.sleep(3000);
      atomicInteger.compareAndSet(0,2019);
    B 라인 의 코드 는 다음 과 같 습 니 다:
    
      atomicInteger.compareAndSet(0,1);
      atomicInteger.compareAndSet(1,0);
    AB 스 레 드 가 동시에 시작 되 었 습 니 다.최종 결과 A 스 레 드 는 2019 로 값 을 수정 할 수 있 지만 그의 수면 과정 에서 B 스 레 드 가 데 이 터 를 바 꾸 었 다 는 것 을 감지 할 수 없습니다.다시 말 하면 A 스 레 드 가 B 스 레 드 에 속 았 다 는 것 입 니 다.
    ABA 문제 해결---AtomicStampedReference.java
    타임 스탬프 가 있 는 원자 인용 은 원자 인용+버 전 번 호 를 통 해 이 루어 집 니 다.지정 한 값 을 수정 할 때마다 해당 하 는 버 전 번 호 는 1 을 추가 합 니 다.인 스 턴 스 는 다음 과 같 습 니 다.
    
      // 0     , 1       
      AtomicStampedReference<Integer> reference = new AtomicStampedReference<>(0, 1);
      reference.getStamp(); //      
      reference.attemptStamp(1,2); //    1,    1    2
    원자 참조
    JUC 에 서 는 AtomicInteger 처럼 구현 클래스 가 정 의 된 것 을 찾 을 수 있 습 니 다.그러나 JUC 에 서 는 이러한 AtomicUser 나 AtomicProduct 과 같은 사용자 정의 형식의 원자 인용 유형 을 제공 하지 않 았 습 니 다.그러나 자바 는 여전히 뒷문 을 제공 합 니 다.바로 원자 인용 유형 입 니 다.
    인 스 턴 스 사용:
    
      User user = getUserById(1);
      AtomicReference<User> userAtomicReference = new AtomicReference<User>();
      user.setUsername("  ");
      userAtomicReference.compareAndSet(user,user);
    이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.

    좋은 웹페이지 즐겨찾기