volatile 확장 문장 - JAVA 고급 다 핵 라인 volatile 원리 와 기교

11902 단어 volatile
출처
 
코드 예제 가 바 뀌 었 습 니 다.
왜 volatile 을 사용 하 는 것 이 동기 화 대가 보다 더 낮 습 니까?동기 화 대 가 는 주로 커버 범위 에 의 해 결정 되 며, 동기 화 커버 범 위 를 낮 출 수 있다 면 프로그램의 성능 을 대폭 향상 시 킬 수 있다.volatile 의 커버 범 위 는 변수 등급 에 불과 하기 때문에 동기 화 대가 가 낮다.
 
volatile 원 리 는 무엇 입 니까?
 
volatile 의 의 미 는 프로세서 에 저 를 작업 메모리 에 넣 지 말고 메 인 메모리 에서 직접 조작 하 라 는 것 입 니 다.(작업 메모 리 는 자바 메모리 모델 참조) 따라서 다 핵 또는 다 중 스 레 드 가 이 변 수 를 방문 할 때 메 인 저장 소 를 직접 조작 합 니 다. 이것 은 본질 적 으로 변 수 를 공유 합 니 다.
volatile 의 장점 은 무엇 입 니까?1. 더 큰 프로그램 스루풋 2. 더 적은 코드 는 다 중 스 레 드 3 을 실현 하고 프로그램의 신축성 이 비교적 좋 으 며 4. 이해 하기 쉬 우 며 너무 높 은 학습 비용 이 필요 없다.
 
volatile 은 어떤 열세 가 있 습 니까?1. 문제 가 발생 하기 쉽다. 2. volatile 연산 에 더러 운 데이터 문제 가 존재 하 는 것 을 설계 하기 어렵다. volatile 은 변수의 가시 성 만 확보 할 수 있 고 원자 성 을 보장 할 수 없다.
volatile 의 race condition 예제:
public class TestRaceCondition {
 private volatile int i = 0;

 public void increase() {
  i++;
 }

 public int getValue() {
  return i;
 }
 /**
  *     
  * @param args
  */
 public static void main(String[] args) {
  final TestRaceCondition c =new TestRaceCondition();
  for (int i =0;i<100;i++) {
   new Thread("thread"+i){
    public void run(){
     c.increase();
    }
   }.start();
  }
  System.out.println("    :100");
  System.out.println("    :"+c.getValue());
 }
}
    :    :100
    :63
 

 
 
다 중 스 레 드 가 increase 방법 을 실행 할 때 그 값 이 선형 으로 증가 할 것 이 라 고 보장 할 수 있 습 니까?
답 은 부정 적 이다.원인: 여기 increase 방법 은 i + +, 즉 i = i + 1 입 니 다.i = i + 1 에 대하 여 다 중 스 레 드 에서 의 연산 자 체 는 i 의 값 을 바 꿔 야 합 니 다. 만약 i 가 메모리 에서 최신 값 을 가 져 왔 으 나 1 과 연산 하지 않 았 다 면 다른 스 레 드 는 연산 결 과 를 i 에 게 수 차례 할당 하 였 으 며, 현재 스 레 드 가 끝 났 을 때 이전의 수 차례 연산 결 과 는 모두 덮어 씁 니 다.즉, 100 회 increase 를 실행 하면 결 과 는 < 100. 일반적으로 이런 상황 은 비교적 높 은 압력 과 병발 상황 에서 만 나타 날 수 있 습 니 다.예시 에서 출력 한 결과 와 같다.
어떻게 이런 상황 을 피 합 니까?상기 문 제 를 해결 하 는 방법: 첫 번 째 방식 은 조작 할 때 동기 화 하 는 것 이다. 이런 방법 은 프로그램의 성능 을 크게 떨 어 뜨리 고 volatile 의 취지 에 어 긋 날 것 이다.
두 번 째 방식 은 하드웨어 원 어 (CAS) 를 사용 하여 비 차단 알고리즘 이 CPU 원 어 에서 변수 수준의 저 소비 동기 화 를 지원 하 는 것 이다.CPU 원 어 - 비교 및 교환 (CompareAndSet), 비 차단 알고리즘 구현.CAS 가 뭐 예요?카 스 는 현대 CPU 가 병렬 프로그램 에 사용 하 는 원 어 동작 입 니 다. 서로 다른 CPU 는 서로 다른 사용 규범 을 가지 고 있 습 니 다. Intel 프로세서 에서 명령 을 통 해 cmpxchg 시 리 즈 를 비교 하고 교환 합 니 다.PowerPC 프로세서 에는 '불 러 오기 및 보존' 과 '조건 저장' 이라는 명령 이 있 습 니 다. 같은 항목 을 실현 합 니 다.MIPS 는 첫 번 째 명령 을 제외 하고 PowerPC 프로세서 와 비슷 합 니 다.CAS 작업 은 세 개의 조작 수 를 포함 합 니 다. 메모리 위치 (V), 예상 원 치 (A) 와 새 값 (B) 은 무엇이 비 차단 알고리즘 입 니까?한 스 레 드 의 실패 나 연결 이 다른 스 레 드 의 실패 나 연결 에 영향 을 주어 서 는 안 됩 니 다.이러한 알고리즘 은 비 차단 (non blocking) 알고리즘 대비 차단 알고리즘 이 라 고 합 니 다. 만약 에 하나의 병행 작업 이 있 으 면 그 중의 한 스 레 드 는 대상 모니터 의 자 물 쇠 를 우선 받 고 다른 스 레 드 가 동기 화 경계 에 도 착 했 을 때 막 힙 니 다.이전 스 레 드 에서 자 물 쇠 를 풀 어야 경쟁 대상 자 물 쇠 를 계속 할 수 있 습 니 다. (물론 이곳 의 경쟁 도 공평 합 니 다. 선착순) CAS 원리: 위치 V 는 값 A 를 포함해 야 한다 고 생각 합 니 다.이 값 을 포함 하면 B 를 이 위치 에 놓 습 니 다.그렇지 않 으 면 이 위 치 를 변경 하지 말고 이 위치의 현재 값 만 알려 주시 면 됩 니 다. CAS 는 예제 (jdk 1.5 및 패키지 AtomicInteger 류 분석 을 사용 합 니 다.
public class AtomicInteger extends Number implements java.io.Serializable {
  private static final long serialVersionUID = 6214790243416807050L;

    // setup to use Unsafe.compareAndSwapInt for updates
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long valueOffset;

    static {
      try {
        valueOffset = unsafe.objectFieldOffset
            (AtomicInteger.class.getDeclaredField("value"));
      } catch (Exception ex) { throw new Error(ex); }
    }

    private volatile int value;

    /**
     * Creates a new AtomicInteger with the given initial value.
     *
     * @param initialValue the initial value
     */
    public AtomicInteger(int initialValue) {
        value = initialValue;
    }

    /**
     * Atomically sets to the given value and returns the old value.
     *
     * @param newValue the new value
     * @return the previous value
     */
    public final int getAndSet(int newValue) {
        for (;;) {
            int current = get();
            if (compareAndSet(current, newValue))
                return current;
        }
    }

    /**
     * Atomically sets the value to the given updated value
     * if the current value {@code ==} the expected value.
     *
     * @param expect the expected value
     * @param update the new value
     * @return true if successful. False return indicates that
     * the actual value was not equal to the expected value.
     */
    public final boolean compareAndSet(int expect, int update) {
  return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }
  /**
     * Atomically sets the value to the given updated value
     * if the current value {@code ==} the expected value.
     *
     * <p>May <a href="package-summary.html#Spurious">fail spuriously</a>
     * and does not provide ordering guarantees, so is only rarely an
     * appropriate alternative to {@code compareAndSet}.
     *
     * @param expect the expected value
     * @param update the new value
     * @return true if successful.
     */
    public final boolean weakCompareAndSet(int expect, int update) {
 return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }

    /**
     * Atomically increments by one the current value.
     *
     * @return the previous value
     */
    public final int getAndIncrement() {
        for (;;) {
            int current = get();
            int next = current + 1;
            if (compareAndSet(current, next))
                return current;
        }
    }

    /**
     * Atomically decrements by one the current value.
     *
     * @return the previous value
     */
    public final int getAndDecrement() {
        for (;;) {
            int current = get();
            int next = current - 1;
            if (compareAndSet(current, next))
                return current;
        }
    }

    /**
     * Atomically adds the given value to the current value.
     *
     * @param delta the value to add
     * @return the previous value
     */
    public final int getAndAdd(int delta) {
        for (;;) {
            int current = get();
            int next = current + delta;
            if (compareAndSet(current, next))
                return current;
        }
    }

    /**
     * Atomically increments by one the current value.
     *
     * @return the updated value
     */
    public final int incrementAndGet() {
        for (;;) {
            int current = get();
            int next = current + 1;
            if (compareAndSet(current, next))
                return next;
        }
    }

    /**
     * Atomically decrements by one the current value.
     *
     * @return the updated value
     */
    public final int decrementAndGet() {
        for (;;) {
            int current = get();
            int next = current - 1;
            if (compareAndSet(current, next))
                return next;
        }
    }

    /**
     * Atomically adds the given value to the current value.
     *
     * @param delta the value to add
     * @return the updated value
     */
    public final int addAndGet(int delta) {
        for (;;) {
            int current = get();
            int next = current + delta;
            if (compareAndSet(current, next))
                return next;
        }
    }
 //......

}

이 방법 은 AtomicInteger 류 의 일반적인 방법 입 니 다. 변 수 를 지정 값 으로 설정 하고 설정 전의 값 을 되 돌려 주 는 역할 을 합 니 다. cpu 원 어 compare AndSet 을 이용 하여 값 의 유일 성 을 보장 합 니 다. 또한 AtomicInteger 류 에서 다른 실 용적 인 방법 도 같은 실현 방식 을 바탕 으로 합 니 다. 예 를 들 어 getAndIncrement, getAndDecrement, getAndAdd 등 입 니 다.
CAS 의미 에 존재 하 는 "ABA 문제"ABA 문 제 는 무엇 입 니까? 만약 에 V 주소 의 A 값 을 처음 읽 은 다음 에 CAS 를 통 해 V 주소 의 값 이 여전히 A 인지 판단 합 니 다. 그렇다면 B 의 값 을 V 주소 에 기록 하고 A 값 을 덮어 씁 니 다. 그러나 의미 에 구멍 이 있 습 니 다. V 의 A 값 을 처음 읽 었 을 때 메모리 V 의 값 은 B 값 으로 변 한 다음 에 CAS 를 실행 하지 않 기 전에 A 값 으로 변 했 습 니 다. 이때 CAS 는다시 실행 할 때 정확 한 것 을 판단 하고 값 을 부여 합 니 다. 이러한 판단 치 는 메모리 가 수정 되 었 는 지 여 부 를 판단 하 는 방식 으로 일부 문제 에 대해 서 는 적용 되 지 않 습 니 다. 이러한 문 제 를 해결 하기 위해 jdk 1.5 는 Atomic Stamped Reference (표 시 된 원자 참조) 를 제공 합 니 다.클래스, 변수 값 을 제어 하 는 버 전 을 통 해 CAS 의 정확성 을 확보 합 니 다. 사실 대부분 값 의 변 화 를 통 해 CAS 는 충분히 사용 할 수 있 습 니 다.
jdk 1.5 원자 팩 소개 (volatile 기반) 가방 의 특색: 1. 일반 원자 수치 유형 AtomicInteger, AtomicLong 은 원자 조작 의 가감 연산 을 제공 합 니 다. 2. 더러 운 데이터 문 제 를 해결 하 는 전형 적 인 모델 - "비교 후 설정" 을 사용 합 니 다.즉, 메 인 저장 소 에 있 는 데이터 가 예상 한 값 과 일치 하 는 지 확인 하고 일치 해 야 업데이트 합 니 다. 3. AtomicReference 를 사용 하면 모든 대상 에 대한 원자 참조 와 할당 을 실현 할 수 있 습 니 다. Double 과 Floa t 를 포함 하지만 이에 대한 계산 은 포함 되 지 않 습 니 다. 부동 소수점 계산 은 동기 키워드 나 Lock 인터페이스 에 의 해 만 이 루어 집 니 다. 4. 배열 요소 의 대상 에 대해 상기 특징 에 부합 되 는 것 도 가능 합 니 다.원자 조작 을 사용 합 니 다. 가방 안에 일부 배열 의 원자 조작 류 Atomic IntegerArray, Atomic LongArray 등 을 제공 합 니 다. 5. 시스템 의 스루풋 과 성능 을 대폭 향상 시 킵 니 다.
본 고 는 [JAVA 100 예] 065, 스 레 드 동기 화 를 상세 하 게 소개 한다.
/**<p>Title:     </p> 
 * <p>Description:                  </p>
 * <p>Copyright: Copyright (c) 2003</p> * <p>Filename: SyThreadDemo.java</p> 
 * @version 1.0 */

/**
 * *<br>
 *    :    *<br>
 *     :      ,     
 */
public class SyThreadDemo {
 public static void main(String[] args) {
  Trade ft = new Trade();
  addThread tt1 = new addThread(ft, "add");
  decThread tt2 = new decThread(ft, "dec");
  tt1.start();
  tt2.start();
 }
}

/**
 * *<br>
 *    :    *<br>
 *     :      ,
 */
class Trade {
 private String transName;
 private double amount;

 /**
  * *<br>
  *     :     *<br>
  *     :String transName      *<br>
  *     :double amount      *<br>
  *     :
  */
 synchronized void update(String transName, double amount) {
  this.transName = transName;
  this.amount = amount;
  System.out.println(this.transName + " " + this.amount);
 }
}

/**
 * *<br>
 *    :     *<br>
 *     :   ,   trade.update()  ,    
 */
class addThread extends Thread {
 private Trade ft;

 addThread(Trade ft, String name) {
  super(name);
  this.ft = ft;
 }

 public void run() {
  for (int i = 0; i < 10; i++)
   ft.update("add", 2000.0);
 }
}

/**
 * *<br>
 *    :     *<br>
 *     :   ,   trade.update()  ,    
 */
class decThread extends Thread {
 private Trade fd;

 decThread(Trade fd, String name) {
  super(name);
  this.fd = fd;
 }

 /**
  * *<br>
  *     :     *<br>
  *     : *<br>
  *     :
  */
 public void run() {
  for (int i = 0; i < 10; i++)
   fd.update("dec", -2000.0);
 }
}

좋은 웹페이지 즐겨찾기