atomic/volatile/synchronized의 차이점은 무엇입니까?
내부적으로 원자성/휘발성/동기화는 어떻게 작동합니까?
다음 코드 블록의 차이점은 무엇입니까?
코드 1
private int counter;
public int getNextUniqueIndex() {
return counter++;
}
코드 2
private AtomicInteger counter;
public int getNextUniqueIndex() {
return counter.getAndIncrement();
}
코드 3
private volatile int counter;
public int getNextUniqueIndex() {
return counter++;
}
volatile
음음음음음음음음음음?음? ★
volatile int i = 0;
void incIBy5() {
i += 5;
}
에 상당하는
Integer i = 5;
void incIBy5() {
int temp;
synchronized(i) { temp = i }
synchronized(i) { i = temp + 5 }
}
그렇죠?내내??? 하면 요?atomic.incrementAndGet()
무일푼으로 synchronized
타래래? 츠요시
그리고 휘발성 변수/원자 변수에 대한 내부 판독과 쓰기의 차이점은 무엇입니까?어떤 기사에서 스레드에 변수의 로컬 복사본이 있다고 읽었는데, 그게 뭐죠?
특히 사내에서의 동작에 대해 질문하고 있습니다.다음은 예를 제시하겠습니다.
동기화 없음
private int counter;
public int getNextUniqueIndex() {
return counter++;
}
기본적으로 메모리에서 값을 읽어내고, 값을 늘린 후 메모리에 되돌립니다.이것은 싱글 스레드에서는 동작하지만, 현재는 멀티 코어, 멀티 CPU, 멀티 레벨 캐시의 시대에서는 올바르게 동작하지 않습니다.우선 레이스 조건(여러 스레드가 동시에 값을 읽을 수 있음)과 가시성 문제가 발생합니다.값은 "로컬" CPU 메모리(일부 캐시)에만 저장되며 다른 CPU/코어(따라서 스레드)에는 표시되지 않을 수 있습니다.그렇기 때문에 스레드 내 변수의 로컬복사를 참조하는 경우가 많습니다.그것은 매우 위험하다.일반적인 스레드 정지 코드를 생각해 봅시다.
private boolean stopped;
public void run() {
while(!stopped) {
//do some work
}
}
public void pleaseStop() {
stopped = true;
}
volatile
로로 합니다.stopped
및 정상적으로 동작합니다.를 수정하는 경우.다른 스레드가 변경되었을 경우stopped
에 의해 변화하다pleaseStop()
의 "Management" (을할 수 .while(!stopped)
루프. 그런데 이것은 스레드를 중단하는 좋은 방법이 아닙니다.사용하지 않고 영원히 실행 중인 스레드를 중지하는 방법 및 특정 자바 스레드를 중지하는 방법을 참조하십시오.
AtomicInteger
private AtomicInteger counter = new AtomicInteger();
public int getNextUniqueIndex() {
return counter.getAndIncrement();
}
AtomicInteger
class는 CAS(compare-and-swap)의 저레벨 CPU 동작을 사용합니다(동기화는 불필요합니다).현재 값이 다른 값과 동일한 경우에만 특정 변수를 수정할 수 있습니다(그리고 성공적으로 반환됨).실행 시getAndIncrement()
실제로는 루프내에서 동작합니다(실제 실장 검증).
int current;
do {
current = get();
} while(!compareAndSet(current, current + 1));
으로 read;value를 하지 않는 이 「」, 「」, 「」, 「」, 「」( 「」)와 하지 않게 ).current
를 읽고 다시 시도합니다.compareAndSet()
는 네이티브 코드(어셈블리)로 구현됩니다.
volatile
하지 않고
private volatile int counter;
public int getNextUniqueIndex() {
return counter++;
}
이 코드는 올바르지 않습니다. 문제시야성 문제)를합니다.volatile
는 다른 할 수 .counter
하지만 여전히 레이스 상태를 유지하고 있습니다.이것은 여러 번 설명되었습니다.사전/사후 증분은 원자성이 아닙니다.
of의 유일한 volatile
는 다른 모든 당사자가 최신 버전의 데이터를 볼 수 있도록 하기 위한 "비활성" 캐시입니다.이것은 대부분의 상황에서 너무 엄격하기 때문에volatile
을 사용하다
volatile
안 함 (2동기화 안 함 (2)
volatile int i = 0;
void incIBy5() {
i += 5;
}
이지만, 「」, 「」가 원인으로, 한층 더 .i
private
레이스 상태는 아직 유효합니다.왜왜 제? ???를 들어에 이하고 있는은 "2개의 스레드"가 될 수 .+ 5
★★★★★★★★★★★★★★★★★」+ 10
단, 변경 내용은 반드시 확인할 수 있습니다.
개의 synchronized
void incIBy5() {
int temp;
synchronized(i) { temp = i }
synchronized(i) { i = temp + 5 }
}
놀랍게도 이 코드도 틀렸습니다.실,,, 그완완완완완 틀틀다다틀틀 ''를 하고 .i
(예: (예: (예:i
는 원시적인에, 하고 있는 것 .Integer
...에누리하다을 사용하다
synchronized(new Object()) {
//thread-safe, SRSLy?
}
의 스레드는 스레드로 수 .synchronized
같은 자물쇠로 막다이 경우(및 코드에서도 마찬가지로) 실행 시마다 잠금 객체가 변경됩니다.synchronized
과가없없 없없없다다
변수 "")를 this
에 대해서는, 않습니다의 동기화에 대해서는, 코드가 아직 올바르지 않습니다. 개의 가 먼저 수 .i
로로 합니다.temp
로 같은 )temp
) 、 ), 、 [ first ] 、 [ first ]에 합니다.i
(예를 들어 1부터6까지)와 다른 한쪽도 같은 동작을 합니다(1부터6까지).
동기화는 읽기부터 값 할당까지 확장되어야 합니다.가 없습니다(:int
) 및 두 atomic).형태입니다.
void synchronized incIBy5() {
i += 5
}
void incIBy5() {
synchronized(this) {
i += 5
}
}
void incIBy5() {
synchronized(this) {
int temp = i;
i = temp + 5;
}
}
변수를 volatile로 선언하는 것은 값을 변경하면 변수의 실제 메모리 스토리지에 즉시 영향을 미친다는 것을 의미합니다.컴파일러는 변수에 대한 참조를 최적화할 수 없습니다.이렇게 하면 1개의 스레드가 변수를 변경할 때 다른 모든 스레드가 새 값을 즉시 볼 수 있습니다(비휘발성 변수에서는 이 값이 보장되지 않습니다).
원자 변수를 선언하면 변수에 대해 수행된 작업이 원자적인 방식으로 수행된다는 것을 보증합니다. 즉, 작업의 모든 하위 단계가 실행되는 스레드 내에서 완료되고 다른 스레드에 의해 중단되지 않습니다.예를 들어, 증분 및 테스트 동작에서는 변수를 증분하여 다른 값과 비교해야 합니다.원자성 동작은 이들 두 단계가 하나의 분할 불가능한/무정전 동작인 것처럼 완료됨을 보증합니다.
변수에 대한 모든 액세스를 동기화하면 한 번에 하나의 스레드만 변수에 액세스할 수 있으며 다른 모든 스레드는 해당 액세스 스레드가 변수에 대한 액세스를 해제할 때까지 대기합니다.
동기화된 액세스는 아토믹액세스와 비슷하지만 아토믹 오퍼레이션은 일반적으로 낮은 수준의 프로그래밍으로 구현됩니다.또한 변수에 대한 일부 액세스만 동기화하고 다른 액세스도 비동기화할 수 있습니다(예: 변수에 대한 모든 쓰기는 동기화하고 읽기는 동기화할 수 없습니다).
원자성, 동기화 및 휘발성은 독립된 속성이지만 일반적으로 변수에 액세스하기 위한 적절한 스레드 협력을 적용하기 위해 조합하여 사용됩니다.
부록 (2016년 4월)
변수에 대한 동기화된 액세스는 일반적으로 모니터 또는 세마포를 사용하여 구현됩니다.이들은 저레벨 뮤텍스(상호 제외) 메커니즘으로 스레드가 변수 또는 코드 블록을 배타적으로 제어할 수 있도록 합니다.다른 모든 스레드도 같은 뮤텍스를 취득하려고 하면 강제로 대기합니다.소유 스레드가 뮤텍스를 해제하면 다른 스레드가 뮤텍스를 획득할 수 있습니다.
부록 (2016년 7월)
개체에서 동기화가 수행됩니다.즉, 클래스의 동기화된 메서드를 호출하면,this
에 의해 잠금이 됩니다.Class
오브젝트 자체
는 ""를 필요가 .this
이치노
즉, 동기화된 메서드(또는 블록)가 다른 오브젝트에 잠겨 있는 경우 여러 스레드에서 동시에 실행할 수 있지만 동시에 동기화된 메서드(또는 블록)를 실행할 수 있는 것은 1개의 스레드뿐입니다.
휘발성:
volatile
을 사용하다 volatile
는 모든 스레드가 캐시가 아닌 메인 메모리에서 변수의 최신 값을 가져오도록 합니다.휘발성 변수에 액세스하기 위해 잠금이 필요하지 않습니다.모든 스레드는 동시에 휘발성 변수 값에 액세스할 수 있습니다.
「」를 사용합니다.volatile
는 휘발성 쓰기가 오류 .
즉, 변수에 대한 변경은 항상 다른 스레드에 표시됩니다.또한 스레드가 변수를 읽을 때 최신의 휘발성 변화뿐만 아니라 변경을 초래한 코드의 부작용도 볼 수 있습니다.
사용 시기: 하나의 스레드는 데이터를 수정하고 다른 스레드는 데이터의 최신 값을 읽어야 합니다. 다른 스레드는 일부 작업을 수행하지만 데이터를 업데이트하지 않습니다.
ATOMIC XXX:
AtomicXXX
클래스는 단일 변수에 대해 잠금 없는 스레드 세이프 프로그래밍을 지원합니다. 것것 。AtomicXXX
예: ★★★★★★★★★★★★★★★」AtomicInteger
는 메모리 오류변수 변경 오류는 는 메모리 불일치 오류/휘발성 변수 변경 부작용을 해결합니다.이러한 오류는 여러 스레드로 접근되었습니다.
사용 시기:여러 스레드가 데이터를 읽고 수정할 수 있습니다.
동기:
synchronized
메서드 또는 코드블록을 보호하기 위해 키워드를 사용합니다.방식을 동기화하면 다음 두 가지 효과가 있습니다.
두 은 불가능합니다.
synchronized
이치노의 스레드가 " " " 를 하고synchronized
, 오브젝트 의 스레드, 오브젝트 이외의 스레드synchronized
첫 번째 스레드가 오브젝트로 완료될 때까지 동일한 오브젝트 블록에 대한 메서드(예: 실행)를 지정합니다.둘째, 'A', 'A'일 때
synchronized
exit에서는, 으로 확립됩니다. occurrents-before-before-before-before-before-contribute of accurrents-before.synchronized
메서드를 지정합니다.이렇게 하면 개체 상태에 대한 변경 내용이 모든 스레드에 표시됩니다.
사용 시기:여러 스레드가 데이터를 읽고 수정할 수 있습니다.비즈니스 로직은 데이터를 갱신할 뿐만 아니라 원자적인 작업도 실행합니다.
AtomicXXX
volatile + synchronized
현현르르르르르르르 르 AmtomicXXX
를 확장하다volatile
+ "변수" +compareAndSet
방법은 있지만 동기화를 사용하지 않습니다.
관련 SE 질문:
참고할 만한 기사: (위 내용은 이 매뉴얼 페이지에서 인용)
https://docs.oracle.com/javase/tutorial/essential/concurrency/sync.html
https://docs.oracle.com/javase/tutorial/essential/concurrency/atomic.html
https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/package-summary.html
두 개의 스레드가 동시에 동기화 블록에 들어갈 수 없다는 것을 알고 있습니다.
두 개의 스레드가 동일한 개체의 동기화된 블록에 두 번 들어갈 수 없습니다.즉, 두 개의 스레드가 서로 다른 개체에서 동일한 블록에 들어갈 수 있습니다.이러한 혼란으로 인해 다음과 같은 코드가 발생할 수 있습니다.
private Integer i = 0;
synchronized(i) {
i++;
}
이것은 매번 다른 개체에서 잠길 수 있기 때문에 예상대로 작동하지 않습니다.
이 값이 동기화 없이 이 atomic.incrementAndGet()이 작동하는 방법보다 클 경우그리고 스레드는 안전합니까?
네, 나사산 안전을 위해 잠금장치를 사용하지 않습니다.
어떻게 작동하는지 자세히 알고 싶다면 코드를 읽어 보십시오.
그리고 내부 판독과 휘발성 변수/원자 변수에 쓰는 것의 차이점은 무엇입니까?
Atomic 클래스는 휘발성 필드를 사용합니다.현장에서는 차이가 없습니다.차이점은 실행되는 작업입니다.Atomic 클래스는 CompareAndSwap 또는 CAS 연산을 사용합니다.
어떤 기사에서 스레드에 변수의 로컬 복사본이 있다는 것을 읽었습니다. 그게 뭐죠?
각 CPU에 다른 CPU와 다를 수 있는 독자적인 캐시된 메모리 뷰가 있는 것을 나타내는 것으로 밖에 생각할 수 없습니다.CPU가 데이터를 일관되게 표시하도록 하려면 스레드 안전 기술을 사용해야 합니다.
이것은, 적어도 1개의 스레드로부터 메모리가 공유되고 있는 경우에 한해 발생합니다.
동기 vs 원자성 vs 휘발성:
- Volatile 및 Atomic은 변수에만 적용되며 Synchronized는 메서드에 적용됩니다.
- 휘발성은 개체의 원자성/일관성이 아닌 가시성을 보장하는 반면, 다른 두 가지 요소는 가시성과 원자성을 보장합니다.
- RAM에 휘발성 변수가 저장되고 액세스 속도는 빨라지지만 스레드 안전 또는 동기화 화이트아웃 synchronized 키워드를 얻을 수 없습니다.
- 동기화된 블록 또는 동기화된 메서드로 구현되었지만 둘 다 구현되지 않았습니다.synchronized 키워드를 사용하여 여러 줄의 코드를 스레드 세이프할 수 있지만, 둘 다 같은 결과를 얻을 수 없습니다.
- 동기화는 같은 클래스 개체 또는 다른 클래스 개체를 잠글 수 있지만 둘 다 잠글 수 없습니다.
제가 놓친 부분이 있으면 정정해 주세요.
volatile + synchronization은 CPU에 대한 여러 명령을 포함하는 동작(스테이트먼트)을 완전히 원자화하는 바보 같은 솔루션입니다.
예를 들어, i:volatile int i = 2; i++는 i = i + 1에 불과하며, 이는 이 문을 실행한 후 i를 메모리의 값 3으로 만듭니다.여기에는 i(2)에 대해 메모리에서 기존 값을 읽고 CPU 어큐뮬레이터 레지스터에 로드한 후 기존 값을 1(어큐뮬레이터에서 1 + 1 = 3)로 증가시킨 값을 메모리에 다시 쓰는 계산이 포함됩니다.값이 i인 경우 휘발성이지만 이러한 작업은 원자성이 충분하지 않습니다.i가 휘발성이라는 것은 메모리에서 단일 읽기/쓰기가 MULTLE이 아닌 원자성이라는 것만 보증합니다.따라서 i++를 중심으로 동기화하여 fool proof atomic 스테이트먼트를 유지할 필요가 있습니다.스테이트먼트에 복수의 스테이트먼트가 포함되어 있는 것에 주의해 주세요.
설명이 충분히 명확하기를 바랍니다.
Java 휘발성 수식자는 스레드 간에 통신이 이루어지도록 보장하는 특수한 메커니즘의 예입니다.한 스레드가 휘발성 변수에 쓰고 다른 스레드가 해당 쓰기를 인식하면 첫 번째 스레드는 해당 휘발성 변수에 쓸 때까지 메모리의 모든 내용을 두 번째 스레드에 알립니다.
원자 연산은 다른 연산의 간섭 없이 단일 단위 태스크에서 수행됩니다.멀티 스레드 환경에서는 데이터의 불일치를 피하기 위해 원자 작업이 필요합니다.
언급URL : https://stackoverflow.com/questions/9749746/what-is-the-difference-between-atomic-volatile-synchronized
'programing' 카테고리의 다른 글
a-la-carte 컴포넌트란?쓸까요? (0) | 2022.08.27 |
---|---|
VUE에서 소품과 함께 v-model 사용.JS (0) | 2022.08.27 |
포인터 주소와 포인터 값을 증가시키는 방법 (0) | 2022.08.27 |
Vuex Getters 메서드 스타일 함수 반환 방법 (0) | 2022.08.27 |
랜덤 영숫자 문자열 생성 방법 (0) | 2022.08.27 |