[java] AtomicInteger의 실제 사용

나는 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예를 들어 Maps에서 값 으로 변경 가능한 버전으로 사용할 수 있습니다 .


답변

예를 들어, 어떤 클래스의 인스턴스를 생성하는 라이브러리가 있습니다. 이러한 인스턴스는 서버로 전송되는 명령을 나타내며 각 명령에는 고유 한 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 에서 가져온 것입니다 .