알리바바 인터뷰 휘발성 물어볼 것입니다, 당신은 정말 아직 것

감사 원래 -http를 참조하십시오 //bjbsair.com/2020-03-27/tech-info/7026/
이야기 휘발성의 이해에 대해?

당신은 기본 구현 메커니즘 휘발성 할 일을 알고 계십니까?

휘발성 변수와 원자 변수의 차이점은 무엇입니까?

휘발성 사용 시나리오, 당신은 두 가지 예제를 줄 수 있습니까?

자바 메모리 모델 --JMM 비교적 상세하게 소개하기 전에 JMM 동시 프로세스 가시성 및 구축이 세 가지 기능을 원자 주문 처리하는 방법을 중심으로 돌아가 , 휘발성 캔 보증이 개 특성 것을, 다음과 같은 구체적 언급이 인터뷰는 키워드를해야합니다.

알리 인터뷰 휘발성 물어볼 것입니다, 당신이 정말로 그것을 될 것인가?

  1. 개념
    =====

자바 휘발성 키워드는, 다른 스레드 액세스를 수정하고 변수를 수정하는 데 사용됩니다 수정 변수입니다.


  1. 자바 메모리 모델 3 곳
    ==================

2.1 가시성

가시성 오류로 인해 가시성는 항상 반대로 우리의 직관에 복잡한 속성입니다. 일반적으로, 우리는 스레드가 쓴 다른 스레드의 값을 확인하기 위해 적시에 읽기 작업을 수행 할 수 있습니다 보장, 때로는 단순히 불가능 할 수 있습니다. 메모리 쓰기 작업의 여러 스레드 간의 가시성을 보장하기 위해, 당신은 동기화 메커니즘을 사용해야합니다.

스레드 간의 가시성 수단 가시성 스레드가 다른 스레드의 상태를 표시 수정 . 즉, 스레드의 수정의 결과입니다. 또 다른 스레드가 즉시 볼 수 있습니다.

자바에서 휘발성, 동기화 및 가시성을 달성 할 수있는 마지막.

2.2 원 자성

자성의 의미는 스레드의 동작, 중간 마개를 실행 또는 분할 또는 전체의 전체 성공 또는 실패를 할 수없는 경우. 예를 들어 A = 0, (a 비 긴 더블)이 조작 불가분, 우리는 동작 원자 것을 말한다. 또 다른 예 : ++,이 조작은 실제로는 A = A + 1, 그 원자 작동되지 않도록, 분할 가능하다. 비 스레드 안전 원자 작업은 문제가, 우리는 원자 작업로를 만들기 위해 (sychronized) 동기식 기술을 사용할 필요가있을 것입니다. 작업은 우리가 원자를 호출, 원자이다. 동시 패키지에서 자바 클래스의 원자의 일부를 제공, AtomicInteger, AtomicLong 같은 AtomicReference.

자바 및 잠금 동기화, 잠금 해제 동작을 보증 자성.

2.3 질서

자바 언어는 동일한에서 한 번에 하나 개의 변수를 허용 "에 의해 질서 보장하기 위해 휘발성 및 동기화 두 개의 키워드 때문에 의미의"명령 재정렬을 금지 "와 자신의 휘발성, 스레드 간의 동기화 작업이입니다 제공 동작 "규칙 수득 잠금 스레드 규칙은 동기 직렬 만 실행될 수 동일한 개체 잠금 두 블록 들고 결정.


  1. 휘발성는 자바 가상 머신에서 제공하는 경량 동기화 메커니즘이다
    =================================
  • 가시성을 보장합니다
  • 원 자성을 보장하지 않습니다
  • 금지 명령의 재 배열 (순서대로 보장)

3.1 빈 소문, 코드 검증

3.1.1 가시성 검증

class MyData {  
    int number = 0;  
    public void add() {  
        this.number = number + 1;  
    }  
}  
  
   // 启动两个线程,一个work线程,一个main线程,work线程修改number值后,查看main线程的number  
   private static void testVolatile() {  
        MyData myData = new MyData();  
       
        new Thread(() -> {  
            System.out.println(Thread.currentThread().getName()+"\t come in");  
            try {  
                TimeUnit.SECONDS.sleep(2);  
                myData.add();  
                System.out.println(Thread.currentThread().getName()+"\t update number value :"+myData.number);  
            } catch (InterruptedException e) {  
                e.printStackTrace();  
            }  
        }, "workThread").start();  
  
        //第2个线程,main线程  
        while (myData.number == 0){  
            //main线程还在找0  
        }  
        System.out.println(Thread.currentThread().getName()+"\t mission is over");        
        System.out.println(Thread.currentThread().getName()+"\t mission is over,main get number is:"+myData.number);  
    }  
}

실행 testVolatile () 메소드, 다음과 같은 출력, 당신은 메인 스레드에서 무한 루프를 찾을 수 주 스레드 설명 된 값은 0입니다

workThread	 execute  
workThread	 update number value :1  
复制代码

수정 휘발성 INT 번호 = 0,, 플러스 키워드 휘발성, 다시 실행할 수 전에, 메인 쓰레드는 하나의 결과를 가져옵니다

workThread	 execute  
workThread	 update number value :1  
main	 execute over,main get number is:1

3.1.2 검증은 원 자성을 보장하지 않습니다

class MyData {  
    volatile int number = 0;  
    public void add() {  
        this.number = number + 1;  
    }  
}  
  
private static void testAtomic() throws InterruptedException {  
  MyData myData = new MyData();  
  
  for (int i = 0; i < 10; i++) {  
    new Thread(() ->{  
      for (int j = 0; j < 1000; j++) {  
        myData.addPlusPlus();  
      }  
    },"addPlusThread:"+ i).start();  
  }  
  
  
  //等待上边20个线程结束后(预计5秒肯定结束了),在main线程中获取最后的number  
  TimeUnit.SECONDS.sleep(5);  
  while (Thread.activeCount() > 2){  
    Thread.yield();  
  }  
  System.out.println("final value:"+myData.number);  
}

실행 testAtomic 마지막 출력 값, 반드시 10000, 10000 값보다 작은 경향이 원하는 값을 발견했다.

final value:9856

바이트 코드 명령어 시간 전 ++ 변환이 네 개의 명령어이기 때문에 왜 이런 일이, 그래서이다

  • 원래의 값을 구하는 getfield
  • iconst_1 스택의 값
  • IADD 증가합니다
  • putfield 메인 메모리에 다시 쓰기 후 작업 IADD

다중 스레드 런타임에 존재하는 것 같은 경쟁, 쓰기 값이 누락 된 경우가있을 수 있습니다.

알리 인터뷰 휘발성 물어볼 것입니다, 당신이 정말로 그것을 될 것인가?

어떻게 원자 문제를 해결하기 위해?

또는 직접 동기화 Automic 원자 클래스를 추가했다.

3.1.3 인증 금지 지시 재배치

컴퓨터가 프로그램을 실행하면 성능을 향상시키기 위해 종종 컴파일러와 프로세서 지침을 재 배열, 일반적으로 다음 세 가지로 나누어 않는이

알리 인터뷰 휘발성 물어볼 것입니다, 당신이 정말로 그것을 될 것인가?

재정렬 명령어 수행 할 때 프로세서가 고려되어야한다 사이에 데이터 의존성을 , 우리는 전화로-경우 시리얼 의미

단일 스레드 환경 구현 및 코드 시퀀스의 결과의 결과들과 일치하는 프로그램의 최종 구현을 보장하지만 다중 스레드 환경 교대 스레드 실행, 컴파일러 최적화 재 배열이 존재하기 때문에, 일관성을 보장 할 수있는 두 개의 스레드에서 사용하는 변수를 판단 할 수 없다 결과는 예측할 수 없습니다.

우리는 멀티 스레드 환경,`최종 출력이 반드시 2 우리의 상상력의 결과가 아닌 경우 휘발성이, 다음 두 변수가 휘발성으로 설정해야한다, 명령 순서 변경을 금지 검증 다음 코드를 사용하는 경향이있다.

public class ReSortSeqDemo {  
  
    int a = 0;  
    boolean flag = false;  
  
    public void mehtod1(){  
        a = 1;  
        flag = true;  
    }  
  
    public void method2(){  
        if(flag){  
            a = a +1;  
            System.out.println("reorder value: "+a);  
        }  
    }  
}

따라서 휘발성 다중 스레드 환경에서 주문 이행 방법의 현상을 방지 명령 재정렬 최적화 금지를 달성한다.

DCL (이중 확인 잠금) 버전 싱글 우리의 가장 일반적인 멀티 스레드 환경의 또 다른 휘발성 disable 명령 재 배열의 특성을 사용하는 것입니다.

public class Singleton {  
  
    private static volatile Singleton instance;  
    
    private Singleton(){}  
    // DCL  
    public static Singleton getInstance(){  
        if(instance ==null){   //第一次检查  
            synchronized (Singleton.class){  
                if(instance == null){   //第二次检查  
                    instance = new Singleton();  
                }  
            }  
        }  
        return instance;  
    }  
}

때문에 존재 명령 재정렬의 두 번 종료 검색 메커니즘은 반드시 스레드 안전하지 않습니다.

왜 ?

이유는 인스턴스 = 새로운 싱글 톤 () 객체를 초기화하는 방법이 정말 원자 조작 아니라,이 세 부분으로 나누어 수행,

  1. 예를 들어 메모리를 할당
  2. 객체를 초기화하는 인스턴스 생성자를 호출
  3. 메모리 공간 할당의 대상 점의 인스턴스 (인스턴스를 실행하는 단계되는 비 - 널)

지시 재정렬 최적화, 2, 3 단계를 판별 할 수없는 정도가 있으면 스텝 2, 3의 데이터 의존 관계는, 가상 머신이 존재하지 않는다. 쓰레드 (A) 및 제 3 동기 블록을 입력 제 2 행하지 않고 수행되는 경우,이 때의 인스턴스는 비 - 널왔다. 처음 검사에서이 시간 스레드 B, 당신은 인스턴스가 null 이외의를 가지고, 그것의 사용으로 돌아갑니다 찾을 수 있지만, 이번에는 자연스럽게 잘못 것 사실 인스턴스를 초기화되지 않았습니다. 우리는 휘발성으로 수정 제한 명령 재 배열 객체 인스턴스에이 그래서 (휘발성 이중 잠금 감지 기능을 사용하기 전에 5 JDK 문제가있다).


  1. 원리
    =====

휘발성 및 스레드의 가시성이 특정 질서를 제공하지만, 자성을 보장 할 수 없습니다 보장 할 수 있습니다. 기본 JVM 구현에서, 메모리 배리어에 기초한다.

  • 때 비 휘발성 읽기 및 쓰기 변수, 변수는 CPU 캐시 각 스레드 메모리 복사 시작. 컴퓨터가 여러 CPU가있는 경우, 각각의 스레드는 각각의 스레드가 다른 CPU의 캐시로 복사 할 수 있다는 것을 의미 다른 CPU에 처리 될 수있다
  • 변수의 선언은 JVM의 보장하지만 각 읽기 변수가 메모리에서 읽어, CPU 캐시를 쓸 때, 그래서, 휘발성 변수의 가시성의 의심의 여지가 없을 것이다이 단계를 건너 뛸 쓰기 작업에 추가됩니다 휘발성 스토어 배리어 명령, 메인 메모리 공유 변수 리프레시 위로의 작업 메모리, 판독시 휘발성 변수, 기록 동작 후에 부하 차단 명령을 추가한다은 메인 메모리로부터 상기 공유 변수를 읽고;

휘발성 CPU에보기에 JIT 컴파일러 hsdis 도구 조립 명령에 의해 생성하기 뭔가를 쓸 것이다, 또는 실시 형태의 단일 모드의 상단을 볼 수 있습니다

알리 인터뷰 휘발성 물어볼 것입니다, 당신이 정말로 그것을 될 것인가?

(PS : 남쪽 너무 내 Javaer에 대한 특정 어셈블리 지침,하지만 우리는 JVM 바이트 코드를 알 수 putstatic 의미는 정적 변수의 값을 설정하는 것입니다, 다음이 putstatic 인스턴스는, 코드의 라인 (17)의 더 많은 결정 정보가 말한다 모든 종류의로, 물론 이젠 그만. 인스턴스에 할당되고, 잠금 ADD1 읽기했다고한다 발견된다. 여기가 ...이 두 www.jianshu.com/p/6ab7c3db1 볼 수 있습니다, www.cnblogs.com/xrq730/ P / 70 ...)

이상의 변수가 공유 할 때 휘발성 쓰기 어셈블러 코드의 두 번째 줄, 문장 부호 수단은 원래 값에 제로를 추가 ADDLs의 추가 명령 전에 잠금 수정했습니다. 찰스 IA-32 아키텍처 소프트웨어 개발자 설명서 프로그램으로 멀티 코어 프로세서 잠금 접두사 명령은 두 가지로 이어질 것입니다 :

  • 현재 데이터 프로세서 캐시 라인은 시스템 메모리에 다시 기록된다
  • 데이터가 유효하지 않은 곳에 다시 쓰기 캐시 메모리 조작은 다른 CPU의 메모리 주소를 발생

그것은 휘발성 "메모리 표시"특성 "명령의 재 배열을 방지하기 위해"잠금을 실현


  1. 사용 시나리오
    =======

당신은 제한 어떤 경우에는 휘발성 변수 대신 잠금을 사용할 수 있습니다. 스레드 안전 휘발성 변수가 이상적입니다 들어, 다음과 같은 두 가지 조건을 충족해야합니다 :

  • 변수에 쓰기는 현재의 값에 의존하지 않는다
  • 이 변수는 다른 변수 불변에 포함되지

사실, 필요 휘발성 사용하지 않는, 자성 장면을 보장합니다.


  1. 휘발성 성능
    ==============

휘발성 판독 성능은 거의 정상 변수 동일한 소비하지만, 느린 기록 동작은, 상기 프로세서는 비 순차적 명령어 처리가 발생하지 않도록하기 위해, 네이티브 코드의 많은 메모리 배리어 명령으로 삽입하는 것이 필요하다.

참조 "적절한 사용 volaitle 변수"단어의 텍스트 :

특히 JVM의 내부 동작의 관점에서, "X는 항상 빠른 Y보다"등, 정확하고 종합적인 평가를하기 어려운. (예를 들어, 어떤 경우 JVM은 완전히 어려운 비교하게 잠금 장치를 제거 할 수 있습니다 추상적 인 휘발성 오버 헤드 동기화.) 즉, 현재의 프로세서 아키텍처의 대부분에, 휘발성 읽기 오버 헤드가 매우 낮입니다 - 거의 비 휘발성 읽기 작업. 정의 된 메모리 (메모리 울타리)를 구현해야 할 필요성의 가시성을 보장하기 때문에, 더 많은 비 휘발성 쓰기보다는 휘발성 쓰기 오버 헤드가, 심지어, 총 지출은 락 취득에 비해 휘발성이 낮은 남아있다.

휘발성 잠금 작업이 원인 방해하고 싶지 않기 때문에, 휘발성, 휘발성 잠금 장치의 안전한 사용의 경우 확장 성이 뛰어난 기능을 제공 할 수 있습니다. 읽기 작업의 수는 잠금에 비해 쓰기보다 많은 경우, 휘발성 변수는 종종 동기화의 성능 오버 헤드를 줄일 수 있습니다.

원저는 0 출판 · 원의 칭찬 0 · 272 조회수

추천

출처blog.csdn.net/zxjoke/article/details/105139584