나는 AtomicInteger와 다른 원자 변수가 동시 액세스를 허용한다는 것을 이해합니다. 이 클래스는 어떤 경우에 일반적으로 사용됩니까?
답변
두 가지 주요 용도가 있습니다 AtomicInteger
.
-
incrementAndGet()
많은 스레드에서 동시에 사용할 수 있는 원자 카운터 ( 등) -
비 차단 알고리즘을 구현하기 위해 비교 및 스왑 명령 (
compareAndSet()
)을 지원하는 프리미티브입니다 .다음은 Brian Göetz의 Java Concurrency In Practice 의 비 차단 난수 생성기 예제입니다 .
public class AtomicPseudoRandom extends PseudoRandom { private AtomicInteger seed; AtomicPseudoRandom(int seed) { this.seed = new AtomicInteger(seed); } public int nextInt(int n) { while (true) { int s = seed.get(); int nextSeed = calculateNext(s); if (seed.compareAndSet(s, nextSeed)) { int remainder = s % n; return remainder > 0 ? remainder : remainder + n; } } } ... }
보시다시피 기본적으로와 거의 같은 방식으로 작동
incrementAndGet()
하지만calculateNext()
증분 대신 임의 계산 ( )을 수행 하고 반환하기 전에 결과를 처리합니다.
답변
내가 생각할 수있는 가장 간단한 예는 원자 연산을 증가시키는 것입니다.
표준 정수로 :
private volatile int counter;
public int getNextUniqueIndex() {
return counter++; // Not atomic, multiple threads could get the same result
}
AtomicInteger로 :
private AtomicInteger counter;
public int getNextUniqueIndex() {
return counter.getAndIncrement();
}
후자는 모든 액세스 동기화에 의존하지 않고 간단한 돌연변이 효과 (특히 계산 또는 고유 색인)를 수행하는 매우 간단한 방법입니다.
더 복잡한 동기화가없는 로직을 사용하여 사용할 수있다 compareAndSet()
낙관적 잠금의 유형으로 -이에 따라 현재 값, 계산 결과를 얻을,이 결과를 설정 IFF에 값이 여전히 다른, 계산을 다시 시작하는 데 사용되는 입력 -하지만, 계산 예제는 매우 유용하며 AtomicIntegers
여러 스레드가 관련되어 있다는 힌트가 있으면 계산 및 VM 전체 고유 생성기에 자주 사용 됩니다. 왜냐하면 작업하기가 너무 쉽기 때문에 평범한 사용을 조기에 최적화하는 것으로 간주합니다. ints
.
거의 항상 같은 선언 ints
과 적절한 synchronized
선언으로 동일한 동기화 보장을 얻을 수 있지만 AtomicInteger
스레드 안전성은 모든 메소드의 가능한 인터리빙 및 모니터에 대해 걱정할 필요없이 실제 객체 자체에 내장되어 있다는 것입니다. 그 int
값 에 액세스합니다 . 전화를 걸 때 실수로 스레드 안전을 위반하여 getAndIncrement()
돌아올 때나 i++
기억할 때 (또는 미리) 올바른 모니터 세트를 얻는 것보다 훨씬 어렵습니다 .
답변
AtomicInteger의 메소드를 보면 int의 일반적인 작업에 해당하는 경향이 있음을 알 수 있습니다. 예를 들어 :
static AtomicInteger i;
// Later, in a thread
int current = i.incrementAndGet();
이 스레드 안전 버전입니다.
static int i;
// Later, in a thread
int current = ++i;
방법은 다음과 같이지도 :
++i
되어 i.incrementAndGet()
i++
있습니다 i.getAndIncrement()
--i
입니다 i.decrementAndGet()
i--
입니다 i.getAndDecrement()
i = x
입니다 i.set(x)
x = i
입니다x = i.get()
compareAndSet
또는 같은 다른 편리한 방법이 있습니다addAndGet
답변
의 주요 사용은 AtomicInteger
다중 스레드 상황에 있고 당신이 사용하지 않고 정수에 스레드 안전 작업을 수행해야 할 때입니다 synchronized
. 기본 유형에 대한 지정 및 검색 int
은 이미 원 자성이지만 AtomicInteger
원 자성이 아닌 많은 연산이 제공됩니다 int
.
가장 간단한 것은 getAndXXX
또는 xXXAndGet
입니다. 예를 들어 getAndIncrement()
, i++
실제로는 검색, 추가 및 할당이라는 세 가지 작업에 대한 단축이기 때문에 원자가 아닌 원자에 해당합니다 . compareAndSet
세마포어, 잠금 장치, 래치 등을 구현하는 데 매우 유용합니다.
를 사용하면 AtomicInteger
동기화를 사용하여 동일한 것을 수행하는 것보다 빠르고 읽기 쉽습니다.
간단한 테스트 :
public synchronized int incrementNotAtomic() {
return notAtomic++;
}
public void performTestNotAtomic() {
final long start = System.currentTimeMillis();
for (int i = 0 ; i < NUM ; i++) {
incrementNotAtomic();
}
System.out.println("Not atomic: "+(System.currentTimeMillis() - start));
}
public void performTestAtomic() {
final long start = System.currentTimeMillis();
for (int i = 0 ; i < NUM ; i++) {
atomic.getAndIncrement();
}
System.out.println("Atomic: "+(System.currentTimeMillis() - start));
}
Java 1.6이 설치된 PC에서 원자 테스트는 3 초 안에 실행되는 반면 동기화 된 테스트는 약 5.5 초 안에 실행됩니다. 여기서 문제는 동기화 작업 ( notAtomic++
)이 실제로 짧다는 것입니다. 따라서 동기화 비용은 작업과 비교하여 실제로 중요합니다.
원 자성 외에 AtomicInteger는 Integer
예를 들어 Map
s에서 값 으로 변경 가능한 버전으로 사용할 수 있습니다 .
답변
예를 들어, 어떤 클래스의 인스턴스를 생성하는 라이브러리가 있습니다. 이러한 인스턴스는 서버로 전송되는 명령을 나타내며 각 명령에는 고유 한 ID가 있어야하므로 각 인스턴스에는 고유 한 정수 ID가 있어야합니다. 여러 스레드가 동시에 명령을 보낼 수 있으므로 AtomicInteger를 사용하여 해당 ID를 생성합니다. 다른 방법은 일종의 잠금 및 일반 정수를 사용하는 것이지만 느리고 우아하지는 않습니다.
답변
gabuzo가 말했듯이 때로는 참조로 int를 전달하려고 할 때 AtomicIntegers를 사용합니다. 아키텍처 특정 코드가있는 내장 클래스이므로 신속하게 코딩 할 수있는 MutableInteger보다 쉽고 최적화되어 있습니다. 그것은 수업을 학대하는 느낌입니다.
답변
Java 8에서 원자 클래스는 두 가지 흥미로운 기능으로 확장되었습니다.
- int getAndUpdate (IntUnaryOperator updateFunction)
- int updateAndGet (IntUnaryOperator updateFunction)
둘 다 원자 값의 업데이트를 수행하기 위해 updateFunction을 사용하고 있습니다. 차이점은 첫 번째 값은 이전 값을 반환하고 두 번째 값은 새 값을 반환한다는 것입니다. updateFunction은 표준보다 더 복잡한 “비교 및 설정”작업을 수행하도록 구현 될 수 있습니다. 예를 들어 원자 카운터가 0 아래로 떨어지지 않는지 확인할 수 있습니다. 일반적으로 동기화가 필요하며 여기에 코드에 잠금이 없습니다.
public class Counter {
private final AtomicInteger number;
public Counter(int number) {
this.number = new AtomicInteger(number);
}
/** @return true if still can decrease */
public boolean dec() {
// updateAndGet(fn) executed atomically:
return number.updateAndGet(n -> (n > 0) ? n - 1 : n) > 0;
}
}
코드는 Java Atomic Example 에서 가져온 것입니다 .