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);
}
}
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Java에서 volatile 키워드의 역할 및 사용법 상세 설명volatile 키워드의 역할은 시스템의 모든 라인이 이 키워드 수식에 대한 변수를 공유하고 볼 수 있도록 하는 것이다. 공유 변수가volatile로 수식될 때, 수정된 값이 메모리에 즉시 업데이트되고, 다른 라인이...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.